简体   繁体   English

xarray:具有低开销轴坐标变换的极坐标 pcolormesh

[英]xarray: polar pcolormesh with low-overhead axis coordinate transformation

I'm trying to plot a two-dimensional xarray DataArray representing a variable parametrised in polar coordinates.我试图绘制一个二维 xarray DataArray表示在极坐标中参数化的变量。 Important : the theta coordinate is in degree, not in radian.重要提示theta坐标是度数,而不是弧度。 The following snippet creates an example data set:以下代码段创建了一个示例数据集:

import numpy as np
import xarray as xr

res_theta = 20
thetas = np.arange(0, 360, res_theta)
res_r = 0.1
rs = np.arange(0, 1, res_r)
data = np.random.random((len(thetas), len(rs)))
my_da = xr.DataArray(
    data,
    coords=(thetas, rs),
    dims=("theta", "r"),
)

I would like to plot this data as a polar pcolormesh .我想将此数据绘制为极坐标pcolormesh I also would like to rely on xarray's plotting routines to benefit from as many features as possible (faceting, plot customisation, etc.).我还想依靠 xarray 的绘图例程来从尽可能多的功能中受益(分面、绘图自定义等)。 Matplotlib's polar projection assumes that the theta angle is given in radian: if I go for the straightforward solution, I first have to convert my theta coordinates to radian, but I don't want to modify the array in-place. Matplotlib 的极坐标投影假设theta角以弧度给出:如果我寻求直接的解决方案,我首先必须将我的theta坐标转换为弧度,但我不想就地修改数组。 I haven't found a better way than copying the array and converting the copy's theta , like this for instance:我还没有找到比复制数组并转换副本的theta更好的方法,例如:

def pcolormesh_polar_expensive(da, *args, **kwargs):
    da_tmp = da.copy()  # I'd like to avoid that
    
    # Get x value
    try:
        x = args[0]
    except IndexError:
        x = da_tmp.dims[0]
    
    da_tmp[x] = np.deg2rad(da_tmp[x])

    try:
        subplot_kws = kwargs["subplot_kws"]
    except KeyError:
        subplot_kws = {}
    
    return da_tmp.plot.pcolormesh(
        *args, 
        subplot_kws=dict(projection="polar"),
        **kwargs
    )

This produces the desired plot:这将产生所需的情节:

pcolormesh_polar_expensive(my_da, "theta", "r")

预期情节

The Actual Problem实际问题

I however would like to avoid duplicating the data: my actual data sets are much larger than that.然而,我想避免重复数据:我的实际数据集比这大得多。 I made some research and found out about Matplotlib's transformation pipeline, and I have the feeling that I could use it to dynamically insert this transformation in plotting routines, but I couldn't get anything to work properly so far.我做了一些研究并发现了 Matplotlib 的转换管道,我觉得我可以用它在绘图例程中动态插入这个转换,但到目前为止我无法正常工作。 Does anybody have an idea of how I could proceed?有没有人知道我该如何继续?

Thanks to @kmuehlbauer's suggestion and a careful examination of the xarray.DataArray.assign_coords() docs , I managed to produce exactly what I wanted.感谢@kmuehlbauer 的建议和对xarray.DataArray.assign_coords()文档的仔细检查,我设法产生了我想要的。

First, I modified my test data to also include unit metadata:首先,我修改了我的测试数据以包含单元元数据:

import numpy as np
import xarray as xr
import pint

ureg = pint.UnitRegistry()

res_r = 0.1
rs = np.arange(0, 1, res_r)
res_theta = 20
thetas = np.arange(0, 360, res_theta)
data = np.random.random((len(rs), len(thetas)))
my_da = xr.DataArray(
    data,
    coords=(rs, thetas),
    dims=("r", "theta"),
)
my_da.theta.attrs["units"] = "deg"

Then, I improved the kwargs processing to automate unit conversion and created an extra coordinate associated to the theta dimension:然后,我改进了 kwargs 处理以自动进行单位转换,并创建了一个与theta维度相关联的额外坐标:

def pcolormesh_polar_cheap(da, r=None, theta=None, add_labels=False, **kwargs):
    if r is None:
        r = da.dims[0]
    if theta is None:
        theta = da.dims[1]
    
    try:
        theta_units = ureg.Unit(da[theta].attrs["units"])
    except KeyError:
        theta_units = ureg.rad

    if theta_units != ureg.rad:
        theta_rad = f"{theta}_rad"
        theta_rad_values = ureg.Quantity(da[theta].values, theta_units).to(ureg.rad).magnitude
        da_plot = da.assign_coords(**{theta_rad: (theta, theta_rad_values)})
        da_plot[theta_rad].attrs = da[theta].attrs
        da_plot[theta_rad].attrs["units"] = "rad"
    else:
        theta_rad = theta
        da_plot = da
    
    kwargs["x"] = theta_rad
    kwargs["y"] = r
    kwargs["add_labels"] = add_labels

    try:
        subplot_kws = kwargs["subplot_kws"]
    except KeyError:
        subplot_kws = {}
    subplot_kws["projection"] = "polar"
    
    return da_plot.plot.pcolormesh(
        **kwargs,
        subplot_kws=subplot_kws,
    )

A very important point here is that assign_coords() returns a copy of the data array it's called from, and this copy's values actually reference the original array, thus adding no memory cost other than the creation of the extra coordinate.这里非常重要的一点是, assign_coords()返回调用它的数据数组的副本,并且该副本的值实际上引用了原始数组,因此除了创建额外坐标之外没有增加任何内存成本。 Modifying the data array in-place as suggested by @kmuehlbauer is straightforward (just replace da_plot = da.assign_coords(...) with da = da.assign_coords(...) ).按照@kmuehlbauer 的建议就地修改数据数组很简单(只需将da_plot = da.assign_coords(...)替换为da = da.assign_coords(...) )。

We then get the same plot (without axis labels, since I changed the defaults so as to hide them):然后我们得到相同的图(没有轴标签,因为我更改了默认值以隐藏它们):

pcolormesh_polar_cheap(my_da, r="r", theta="theta")

示例极坐标图

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM