4

我正在尝试创建一个带有两个 holoviews 对象的仪表板:一个包含 xarray 变量列表的面板对象,以及一个在输入中采用所选变量的 hvplot对象,如下所示:pn.widgets.Select

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

以下是特定变量(同时具有时间和高度维度)的示例网格: 在此处输入图像描述

当我从面板小部件中选择一个变量时,我希望更新地图: 在此处输入图像描述 我尝试将其作为动态地图执行,如下所示:

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.

从面板小部件中为 hvplot 选择变量似乎很常见。使用pyviz完成此任务的最佳方法是什么?

如果它有用,这是我的完整尝试 Jupyter Notebook

4

2 回答 2

3

因为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)

生产: 在此处输入图像描述

这是完整的笔记本

于 2019-02-27T14:33:36.807 回答
1

所以这里有一个长答案和一个简短答案。让我们从简短的回答开始,即不需要为数据变量创建自定义选择小部件,因为 hvPlot 允许自动在多个数据变量之间进行选择,所以如果您将其更改为:

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,可让您选择非空间维度和数据变量,您现在可以将其嵌入面板中,无需额外工作。如果这就是您关心的全部内容,请停在这里,因为我们即将进入一些内部结构,希望能提供更好的理解。

让我们假设 hvPlot 不允许在数据变量之间进行选择,那么我们会怎么做?所以你必须知道的主要事情是 HoloViews 允许链接 DynamicMaps 但不允许嵌套它们。这可能有点难以理解,但我们会将问题分解为多个步骤,然后看看我们如何实现我们想要的。那么给我们提供情节的事件链是什么?

  1. 选择数据变量
  2. 在非空间维度上应用 groupby
  3. 对每个 QuadMesh 应用光栅化

如您所知,hvPlot 为我们处理了第 2 步和第 3 步,那么我们如何在第 2 步和第 3 步之前注入第 1 步。将来我们计划添加对将面板小部件直接传递到 hvPlot 的支持,这意味着您只需一步即可完成所有操作。由于面板仍然是一个非常新的项目,我将一路指出我们的 API 最终将如何使这个过程变得微不足道,但现在我们必须坚持使用相对冗长的解决方法。在这种情况下,我们必须重新排列操作顺序:

  1. 在非空间维度上应用 groupby
  2. 选择数据变量
  3. 对每个 QuadMesh 应用光栅化

因此,首先我们选择所有数据变量并跳过光栅化:

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 链接到 Selection 小部件,应用 groupby 并被光栅化,我们现在可以将其嵌入到面板中。上面@jbednar 暗示的另一种方法是通过使 hvPlot 调用不是动态的并手动进行时间和高度级别选择来一步完成所有这些工作。我不会在这里讨论它,但它也是一种有效的(如果效率较低)方法。

正如我在上面所暗示的,最终我们还打算让所有 hvPlot 参数变为动态的,这意味着您将能够执行以下操作来将小部件值链接到 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')
于 2019-02-19T16:51:25.583 回答