View 与 Viewable with displaying widget

View vs. Viewable with displaying widget

我正在使用 pyviz 生态系统构建一个交互式仪表板。仪表板的一个特点是基础数据可能会根据小部件 selector 发生变化。下面是一个示例代码,显示了我在让时间小部件滑块出现时遇到的问题:

包版本
面板:0.5.1
参数:1.9.0
全息视图:1.12.3
地理视图:1.6.2

示例

import xarray as xr
import panel as pn
import numpy as np
import param as pm
import holoviews as hv
import geoviews as gv
from matplotlib import cm
import geoviews.tile_sources as gts
from holoviews.operation.datashader import rasterize
from collections import OrderedDict as odict
from holoviews import opts
renderer = hv.renderer('bokeh')
pn.extension()

dset = xr.DataArray(np.random.random((100,100,100)),coords={'X':np.arange(100),'Y':np.arange(100),'T':np.arange(100)},dims=['X','Y','T']).to_dataset(name='test')
dset = gv.Dataset(dset, ['X', 'Y', 'T'], 'test').to(gv.QuadMesh, groupby='T').opts(cmap='viridis', colorbar=True, show_frame=False)

fields = odict([('test','test')])#odict([(v.get('label',k),k) for k,v in source.metadata['fields'].items()])
aggfns = odict([(f.capitalize(),f) for f in ['mean','std','min','max','Pixel Level']])#'count','sum','min','max','mean','var','std']])#,'None (Pixel Level)']])
cmaps  = odict([(n,cm.get_cmap(n)) for n in ['viridis','seismic','cool','PiYG']])
maps   = ['EsriImagery','EsriNatGeo', 'EsriTerrain', 'OSM']
bases  = odict([(name, gts.tile_sources[name].relabel(name)) for name in maps])
gopts  = hv.opts.WMTS(responsive=True, xaxis=None, yaxis=None, bgcolor='black', show_grid=False)


class Explorer_Test(pm.Parameterized):
    field = pm.Selector(fields)
    cmap = pm.Selector(cmaps)
    basemap = pm.Selector(bases)
    data_opacity = pm.Magnitude(1.00)
    map_opacity = pm.Magnitude(1.00)
    agg_fn_ = pm.Selector(aggfns,label='Aggregation**',default='mean')

    @pm.depends('field', 'agg_fn_')
    def aggregator(self):
        field = None if self.field == "counts" else self.field
        return self.agg_fn(field)

    @pm.depends('map_opacity', 'basemap')
    def tiles(self):
        return self.basemap.opts(gopts).opts(alpha=self.map_opacity)

    def viewable(self,**kwargs):
        rasterized = rasterize(dset, precompute=True).opts(colorbar=True, height=800, show_frame=False).apply.opts(cmap=self.param.cmap,alpha=self.param.data_opacity)
        return hv.DynamicMap(self.tiles)*rasterized

explorer_test = Explorer_Test(name="")

当我显示这样的情节时:

panel = pn.Row(pn.Param(explorer_test.param, expand_button=False),explorer_test.viewable())
panel.servable()

时间小部件出现:

鉴于:

panel = pn.Row(pn.Param(explorer_test.param, expand_button=False),explorer_test.viewable)
panel.servable()

在第一个示例中,如果我 select 一个替代数据集(基于 param.Selector 小部件 - 此示例中未显示)它不会重绘图像。然而,在第二个例子中,图像被重新绘制,但我错过了时间滑块。

更新 - 解决方案

这是 James 解决方案的解决方法(谢谢!)。此示例包括更改数据集和变量(在每个数据集中)和时间参数。

import xarray as xr
import panel as pn
import numpy as np
import param as pm
import holoviews as hv
import geoviews as gv
from holoviews.operation.datashader import rasterize
from collections import OrderedDict as odict
renderer = hv.renderer('bokeh')
pn.extension()

#Define Example Datasets
dset1 = xr.merge([xr.DataArray(np.random.random((50,50,50)),coords={'X':np.arange(50),'Y':np.arange(50),'T':np.arange(50)},dims=['X','Y','T']).to_dataset(name='var1'),
                  xr.DataArray(np.random.random((50,50,10))*.1,coords={'X':np.arange(50),'Y':np.arange(50),'T':np.arange(10)},dims=['X','Y','T']).to_dataset(name='var2')])
dset2 = xr.DataArray(np.random.random((50,50,20))*10,coords={'X':np.arange(50)/2.,'Y':np.arange(50)/3.,'T':np.arange(20)},dims=['X','Y','T']).to_dataset(name='var1')
data_dict = {'dset1':dset1,'dset2':dset2}                 

#Plot Datasets
class sel_dset_var():
    def dset1_var1():
        return rasterize(gv.Dataset(dset1.var1, ['X', 'Y', 'T'], 'test1').to(gv.QuadMesh, groupby='T')()).opts(cmap='viridis',colorbar=True, height=200, show_frame=False)
    def dset1_var2():
        return rasterize(gv.Dataset(dset1.var2, ['X', 'Y', 'T'], 'test1').to(gv.QuadMesh, groupby='T')()).opts(cmap='viridis',colorbar=True, height=200, show_frame=False)
    def dset2_var1():
        return rasterize(gv.Dataset(dset2.var1, ['X', 'Y', 'T'], 'test1').to(gv.QuadMesh, groupby='T')()).opts(cmap='viridis',colorbar=True, height=200, show_frame=False)

#Dashboard
class Explorer_Test(pm.Parameterized):
    dset = pm.Selector(odict([('Dataset1','dset1'),('Dataset2','dset2')]),default='dset1')
    varss = pm.Selector(list(dset1.data_vars),default=list(dset1.data_vars)[0])
    time1 = pm.Selector(dset1.var1.coords['T'].values,default=dset1.var1.coords['T'].values[0])

    @pm.depends('dset',watch=True)
    def update_var(self):
        self.param['varss'].objects = list(data_dict[self.dset].data_vars)
        self.param.set_param(varss=list(data_dict[self.dset].data_vars)[0])

    @pm.depends('dset',watch=True)
    def update_var(self):
        self.param['varss'].objects = list(data_dict[self.dset].data_vars)
        self.param.set_param(varss=list(data_dict[self.dset].data_vars)[0])

    def elem(self):
        return getattr(sel_dset_var,self.dset+'_'+self.varss)()

    @pm.depends('varss','dset',watch=True)
    def update_time(self):
        self.param['time1'].objects =data_dict[self.dset][self.varss].dropna(dim='T').coords['T'].values
        self.param.set_param(time1=data_dict[self.dset][self.varss].dropna(dim='T').coords['T'].values[0])

    def elem_yr(self):
        return getattr(self.elem(),'select')(T=self.time1)


    def viewable(self,**kwargs):
        return self.elem_yr

explorer_test = Explorer_Test(name="")
panel = pn.Row(pn.Param(explorer_test.param, expand_button=False),explorer_test.viewable())
panel.servable()

干杯!

这段代码看起来像是源自我的 http://datashader.org/dashboard.html 示例。在我的示例中,viewable() 方法的输出已经是完全动态的,永远不需要重新生成,因为已经在内部链接到影响其显示方式的所有小部件和控件。而如果您将 viewable 作为方法名称传递给 Panel(而不是调用该方法的 result),则您要求 Panel 随时为您调用 viewable()它确定初始调用的结果变得陈旧。这种简单的 re-run-the-method 方法适用于非常简单的 all-or-nothing 计算情况,但当对象本身已经是动态的并且特定控件与绘图的特定方面相关联时,这里并不是很有用。 (为什么在那种情况下你也没有得到时间小部件我不确定;这不是推荐的用法,但我认为它仍然可以为你提供一个小部件。)

无论如何,我认为您不应该尝试使上面的第二种情况起作用,而应尝试使第一种情况起作用。问题不在于缺少滑块,这听起来像是您正在尝试让绘图响应数据源中的变化。幸运的是,这种情况已经在 http://datashader.org/dashboard.html 中的示例中进行了说明; rasterize 动态包装一个方法,returns 显示适当的数据列。您应该能够调整该方法,使其动态反映让用户 select 数据集的其他一些小部件的状态。