从 Matplotlib Raster 到 Geoviews/ Holoviews / hvplot:如何转换 x、y 和 z

From Matplotlib Raster to Geoviews/ Holoviews / hvplot: How to transform x, y and z

我了解 Geoviews 和 Holoviews 具有共同的属性,而 Hvplot 是对所有这三者的高级绘图 API。

现在,来自 Matplotlib,我仍然难以适应在 Geoviews 或 Holoviews 中显示光栅图像所需的参数。

这是一个示例,我正在对空间数据进行核密度估计:

# add coordinates of observations
xy_train = np.vstack([y, x]).T
print(xy_train)
# [[5654810.66920637  413645.79802685]
# [5654712.51814666  412629.87266155]
# [5656120.03682466  411642.74943511]
# ...
# [5656316.96943554  411795.80163676]
# [5656299.73356505  411795.50717494]
# [5655756.85624901  411734.34680852]]

# create mesh
xbins=100j
ybins=100j
xx, yy = np.mgrid[left_bound:right_bound:xbins, 
                bottom_bound:top_bound:ybins]
xy_sample = np.vstack([yy.ravel(), xx.ravel()]).T
# compute Kernel Density here
# ..
kde = KernelDensity(kernel='gaussian', bandwidth=100, algorithm='ball_tree')
kde.fit(xy_train)
# get results
z = np.exp(kde.score_samples(xy_sample))
# reshape results to mesh
zz = z.reshape(xx.shape)
# plot in matplotlib
fig, ax_lst = plt.subplots(111)
levels = np.linspace(zz.min(), zz.max(), 25)
axis.contourf(xx, yy, zz, levels=levels, cmap='viridis')
axis.plot()
plt.show()

显示我的图片:

现在我想使用pyviz环境进行交互式显示和地图叠加,例如使用地理视图。

这在某种程度上有效,但给我一个错误:

xr_dataset = gv.Dataset(hv.Image((xx, yy, zz.T), datatype=['grid']), crs=ccrs.UTM(zone='33N'))

Image02195: Image dimension(s) x and y are not evenly sampled to relative tolerance of 0.001. Please use the QuadMesh element for irregularly sampled data or set a higher tolerance on hv.config.image_rtol or the rtol parameter in the Image constructor.

我仍然可以显示图像(分辨率有点低)。

gv.tile_sources.CartoLight.opts(width=800, height=480) * xr_dataset.to.image(['x', 'y']).opts(cmap='viridis', alpha=0.5)

.. 但是当我尝试在 Geoviews 中创建 FilledContours 时,它似乎不像在 matplotlib 中那样工作:

gv.FilledContours(xx, yy, zz, levels=levels, cmap='viridis')

ValueError: kdims argument expects a Dimension or list of dimensions, specified as tuples, strings, dictionaries or Dimension instances, not a ndarray type. Ensure you passed the data as the first argument.

该文档没有提供太多关于我应该如何格式化维度的信息 (hv.help(gv.FilledContours))。当我需要从 numpy xx/yy 坐标网格 (hv.Image((xx, yy, zz.T), datatype=['grid'])) 创建光栅时,我想我迷路了。

有人可以解释 matplotlib Contourf 和 Holoviews/Geoviews/Hvplot FilledContours 所需的语法差异吗?

[编辑]

我找到了创建轮廓的方法,但尺寸问题仍然存在:

# get xarray dataset, suited for handling raster data in pyviz
xr_dataset = gv.Dataset(hv.Image((xx.T, yy.T, zz.T), bounds=(left_bound,bottom_bound,right_bound,top_bound), 
        kdims=[hv.Dimension('x'),  hv.Dimension('y')], datatype=['grid']), crs=ccrs.UTM(zone='33N'))
# Error: Image06593: Image dimension(s) x and y are not evenly sampled to relative tolerance of 0.001

# create contours from image
gv.FilledContours(xr_dataset)

# plot
gv.tile_sources.EsriImagery.opts(width=800, height=480) * gv.FilledContours(xr_dataset).opts(cmap='viridis', alpha=0.5)

关于 HoloViews/GeoViews 元素要了解的主要事情是数据几乎总是指定为第一个参数,这与 matplotlib 不同,后者通常使用多个参数指定数据。在您的情况下,您已经拥有 Image 的正确语法,但没有将其应用于其他元素。因此,为了使这个具体化,构建一个图像,你会这样做:

img = gv.Image((xx, yy, zz.T), crs=ccrs.UTM(zone='33N'))

然而,由于您拥有 2D 坐标数组而不是 Image 期望的 1D 坐标(在下一个版本中这将出错),您实际上有一个 QuadMesh,它以相同的方式构建:

qmesh = gv.QuadMesh((xx, yy, zz.T), crs=ccrs.UTM(zone='33N'))

地理视图也是如此FilledContours:

contours = gv.FilledContours((xx, yy, zz.T), crs=ccrs.UTM(zone='33N'))

总而言之,HoloViews 元素和 matplotlib 调用之间的区别在于,HoloViews 是数据的轻量级包装器,它允许您通过将每个坐标和值数组分配给键或值维度来赋予它们语义,而matplotlib 使映射更加明确。

HoloViews 理解多种格式来定义像您这样的网格化数据,最简单的是 x/y-coordinate 数组和值数组的元组,它还理解 xarray 对象和不同数组的字典,看起来像这个:

contours = gv.FilledContours({'x': xx, 'y': yy, 'z': zz.T}, ['x', 'y'], 'z', crs=ccrs.UTM(zone='33N'))

在这种格式中,我们可以明确地看到 'x' 和 'y' 坐标数组如何映射到键维度以及 'z' 值数组如何映射到值维度。