Panel/Hvplot 变量变化时的交互
Panel/Hvplot interaction when variable is changing
我正在尝试创建一个包含两个全息视图对象的仪表板:一个 panel pn.widgets.Select
object that contains a list of xarray variables, and a hvplot 对象,它在输入时采用 selected 变量,如下所示:
def hvmesh(var=None):
mesh = ds[var].hvplot.quadmesh(x='x', y='y', rasterize=True, crs=crs,
width=600, height=400, groupby=list(ds[var].dims[:-2]), cmap='jet')
return mesh
这是特定变量(同时具有时间和高度维度)的示例网格的样子:
我想在 select 来自面板小部件的变量时更新地图:
我试着把它做成动态地图,像这样:
from holoviews.streams import Params
import holoviews as hv
var_stream = Params(var_select, ['value'], rename={'value': 'var'})
mesh = hv.DynamicMap(hvmesh, streams=[var_stream])
但是当我尝试显示地图时,我得到:
Exception: Nesting a DynamicMap inside a DynamicMap is not supported.
select 面板小部件中的 hvplot 变量似乎很常见。使用 pyviz 完成此操作的最佳方法是什么?
如果它有用,这是我的full attempt Jupyter Notebook。
所以这里一长一短。让我们从简短的答案开始,即不需要为数据变量创建自定义 select 小部件,因为 hvPlot 允许自动在多个数据变量之间进行 selecting,因此如果将其更改为:
rasterized_mesh = ds[time_vars].hvplot.quadmesh(
x='x', y='y', z=time_vars[::-1], crs=crs, width=600, height=400,
groupby=list(ds[var].dims[:-2]), rasterize=True, cmap='jet')
您将获得一个 DynamicMap,它允许您 select 非空间维度和数据变量,您现在可以将其嵌入到您的面板中,无需额外的工作。如果这就是您关心的全部内容停在这里,因为我们即将进入一些内部结构,希望能提供更好的理解。
让我们暂时假设 hvPlot 不允许在数据变量之间 selecting,那么我们会怎么做?所以你必须知道的主要事情是 HoloViews 允许链接 DynamicMaps 但不允许嵌套它们。这可能有点难以理解,但我们会将问题分解为多个步骤,然后看看我们如何才能实现我们想要的。那么给我们情节的事件链是什么?
- Select一个数据变量
- 在非空间维度上应用 groupby
- 对每个 QuadMesh 应用光栅化
如您所知,hvPlot 为我们处理了第 2 步和第 3 步,那么我们如何在第 2 步和第 3 步之前注入第 1 步。将来我们计划添加对将面板小部件直接传递到 hvPlot 的支持,这意味着您只需一步即可完成所有操作。由于面板仍然是一个非常新的项目,我将一路指出我们的 API 如何最终使这个过程变得微不足道,但现在我们必须坚持相对冗长的解决方法。在这种情况下,我们必须重新排列操作顺序:
- 在非空间维度上应用 groupby
- Select一个数据变量
- 对每个 QuadMesh 应用光栅化
因此我们首先 select 所有 数据变量并跳过光栅化:
meshes = ds[time_vars].hvplot.quadmesh(
x='x', y='y', z=time_vars, crs=crs, width=600, height=400,
groupby=list(ds[var].dims[:-2]))
现在我们有了一个 DynamicMap,其中包含我们可能想要显示的所有数据,我们可以应用下一个操作。在这里,我们将使用 hv.util.Dynamic
实用程序,该实用程序可用于在注入流值时对 DynamicMap 进行链接操作。特别是在这一步中,我们从 var_select
小部件创建一个流,它将用于重新索引我们网格中的 QuadMesh
DynamicMap
:
def select_var(obj, var):
return obj.clone(vdims=[var])
var_stream = Params(var_select, ['value'], rename={'value': 'var'})
var_mesh = hv.util.Dynamic(meshes, operation=select_var, streams=[var_select])
# Note starting in hv 1.12 you'll be able to replace this with
# var_mesh = meshes.map(select_var, streams=[var_select])
# And once param 2.0 is out we intend to support
# var_mesh = meshes.map(select_var, var=var_select.param.value)
现在我们有了一个 DynamicMap,它可以响应小部件中的变化,但还没有对其进行栅格化,因此我们可以手动应用 rasterize
操作:
rasterized_mesh = rasterize(var_mesh).opts(cmap='jet', width=600, height=400)
现在我们有一个 DynamicMap,它 linked 到 Selection 小部件,应用 groupby 并栅格化,我们现在可以将其嵌入面板中。上面@jbednar 暗示的另一种方法是通过使 hvPlot 调用不是动态的并手动执行时间和高度级别 selection 来一步完成所有操作。我不会在这里讨论它,但它也是一种有效的(如果效率较低)方法。
正如我在上面暗示的那样,最终我们还打算让所有 hvPlot 参数都变成动态的,这意味着您将能够对 link hvPlot 关键字参数的小部件值:
ds[time_vars].hvplot.quadmesh(
x='x', y='y', z=var_select.param.value, rasterize=True, crs=crs,
width=600, height=400, groupby=list(ds[var].dims[:-2]), cmap='jet')
因为 groupby
随着每个变量的选择而变化,变量列表不能传递给 hvplot
。因此,一种解决方案是每次选择新变量时重新创建绘图。这有效:
import holoviews as hv
from holoviews.streams import Params
def plot(var=None, tiles=None):
var = var or var_select.value
tiles = tiles or map_select.value
mesh = ds[var].hvplot.quadmesh(x='x', y='y', rasterize=True, crs=crs, title=var,
width=600, height=400, groupby=list(ds[var].dims[:-2]),
cmap='jet')
return mesh.opts(alpha=0.7) * tiles
def on_var_select(event):
var = event.obj.value
col[-1] = plot(var=var)
def on_map_select(event):
tiles = event.obj.value
col[-1] = plot(tiles=tiles)
var_select.param.watch(on_var_select, parameter_names=['value']);
map_select.param.watch(on_map_select, parameter_names=['value']);
col = pn.Column(var_select, map_select, plot(var_select.value) * tiles)
生产:
这里是full notebook。
我正在尝试创建一个包含两个全息视图对象的仪表板:一个 panel pn.widgets.Select
object that contains a list of xarray variables, and a hvplot 对象,它在输入时采用 selected 变量,如下所示:
def hvmesh(var=None):
mesh = ds[var].hvplot.quadmesh(x='x', y='y', rasterize=True, crs=crs,
width=600, height=400, groupby=list(ds[var].dims[:-2]), cmap='jet')
return mesh
这是特定变量(同时具有时间和高度维度)的示例网格的样子:
我想在 select 来自面板小部件的变量时更新地图:
from holoviews.streams import Params
import holoviews as hv
var_stream = Params(var_select, ['value'], rename={'value': 'var'})
mesh = hv.DynamicMap(hvmesh, streams=[var_stream])
但是当我尝试显示地图时,我得到:
Exception: Nesting a DynamicMap inside a DynamicMap is not supported.
select 面板小部件中的 hvplot 变量似乎很常见。使用 pyviz 完成此操作的最佳方法是什么?
如果它有用,这是我的full attempt Jupyter Notebook。
所以这里一长一短。让我们从简短的答案开始,即不需要为数据变量创建自定义 select 小部件,因为 hvPlot 允许自动在多个数据变量之间进行 selecting,因此如果将其更改为:
rasterized_mesh = ds[time_vars].hvplot.quadmesh(
x='x', y='y', z=time_vars[::-1], crs=crs, width=600, height=400,
groupby=list(ds[var].dims[:-2]), rasterize=True, cmap='jet')
您将获得一个 DynamicMap,它允许您 select 非空间维度和数据变量,您现在可以将其嵌入到您的面板中,无需额外的工作。如果这就是您关心的全部内容停在这里,因为我们即将进入一些内部结构,希望能提供更好的理解。
让我们暂时假设 hvPlot 不允许在数据变量之间 selecting,那么我们会怎么做?所以你必须知道的主要事情是 HoloViews 允许链接 DynamicMaps 但不允许嵌套它们。这可能有点难以理解,但我们会将问题分解为多个步骤,然后看看我们如何才能实现我们想要的。那么给我们情节的事件链是什么?
- Select一个数据变量
- 在非空间维度上应用 groupby
- 对每个 QuadMesh 应用光栅化
如您所知,hvPlot 为我们处理了第 2 步和第 3 步,那么我们如何在第 2 步和第 3 步之前注入第 1 步。将来我们计划添加对将面板小部件直接传递到 hvPlot 的支持,这意味着您只需一步即可完成所有操作。由于面板仍然是一个非常新的项目,我将一路指出我们的 API 如何最终使这个过程变得微不足道,但现在我们必须坚持相对冗长的解决方法。在这种情况下,我们必须重新排列操作顺序:
- 在非空间维度上应用 groupby
- Select一个数据变量
- 对每个 QuadMesh 应用光栅化
因此我们首先 select 所有 数据变量并跳过光栅化:
meshes = ds[time_vars].hvplot.quadmesh(
x='x', y='y', z=time_vars, crs=crs, width=600, height=400,
groupby=list(ds[var].dims[:-2]))
现在我们有了一个 DynamicMap,其中包含我们可能想要显示的所有数据,我们可以应用下一个操作。在这里,我们将使用 hv.util.Dynamic
实用程序,该实用程序可用于在注入流值时对 DynamicMap 进行链接操作。特别是在这一步中,我们从 var_select
小部件创建一个流,它将用于重新索引我们网格中的 QuadMesh
DynamicMap
:
def select_var(obj, var):
return obj.clone(vdims=[var])
var_stream = Params(var_select, ['value'], rename={'value': 'var'})
var_mesh = hv.util.Dynamic(meshes, operation=select_var, streams=[var_select])
# Note starting in hv 1.12 you'll be able to replace this with
# var_mesh = meshes.map(select_var, streams=[var_select])
# And once param 2.0 is out we intend to support
# var_mesh = meshes.map(select_var, var=var_select.param.value)
现在我们有了一个 DynamicMap,它可以响应小部件中的变化,但还没有对其进行栅格化,因此我们可以手动应用 rasterize
操作:
rasterized_mesh = rasterize(var_mesh).opts(cmap='jet', width=600, height=400)
现在我们有一个 DynamicMap,它 linked 到 Selection 小部件,应用 groupby 并栅格化,我们现在可以将其嵌入面板中。上面@jbednar 暗示的另一种方法是通过使 hvPlot 调用不是动态的并手动执行时间和高度级别 selection 来一步完成所有操作。我不会在这里讨论它,但它也是一种有效的(如果效率较低)方法。
正如我在上面暗示的那样,最终我们还打算让所有 hvPlot 参数都变成动态的,这意味着您将能够对 link hvPlot 关键字参数的小部件值:
ds[time_vars].hvplot.quadmesh(
x='x', y='y', z=var_select.param.value, rasterize=True, crs=crs,
width=600, height=400, groupby=list(ds[var].dims[:-2]), cmap='jet')
因为 groupby
随着每个变量的选择而变化,变量列表不能传递给 hvplot
。因此,一种解决方案是每次选择新变量时重新创建绘图。这有效:
import holoviews as hv
from holoviews.streams import Params
def plot(var=None, tiles=None):
var = var or var_select.value
tiles = tiles or map_select.value
mesh = ds[var].hvplot.quadmesh(x='x', y='y', rasterize=True, crs=crs, title=var,
width=600, height=400, groupby=list(ds[var].dims[:-2]),
cmap='jet')
return mesh.opts(alpha=0.7) * tiles
def on_var_select(event):
var = event.obj.value
col[-1] = plot(var=var)
def on_map_select(event):
tiles = event.obj.value
col[-1] = plot(tiles=tiles)
var_select.param.watch(on_var_select, parameter_names=['value']);
map_select.param.watch(on_map_select, parameter_names=['value']);
col = pn.Column(var_select, map_select, plot(var_select.value) * tiles)
生产:
这里是full notebook。