【问题标题】:Plot slice through 3-D xarray.Dataset通过 3-D xarray.Dataset 绘制切片
【发布时间】:2021-10-30 19:43:12
【问题描述】:

我想通过 3-D xarray.Dataset 绘制一个切片,如下所示:

这就是我开始的方式(为简单起见,使用教程 air_temperature 数据集)

# import packages
import xarray as xr
import numpy as np

# load dataset
ds = xr.tutorial.open_dataset("air_temperature")

# get slice values
tgt_lon = xr.DataArray(np.linspace(220, 280, num=15), dims="lon")
tgt_lat = xr.DataArray(np.linspace(30, 50, num=15), dims="lat")

# crop to region of interest - this works fine
da = ds.sel(lon=tgt_lon, 
            lat=tgt_lat, 
            method="nearest")

现在我们还有一个 3-D xarray.Dataset。对于二维绘图,我们希望将经度和纬度堆叠到相对于零点的距离数组(此处:角 x0/y0,如图所示)。

# zero point: lat_min/lon_min
lon_orig = da.lon.min().values
lat_orig = da.lat.min().values

# stack longitude and latitude -- gives tuple for dist
sta = da.stack(dist=('lon', 'lat')) 

... 然后我们可以通过循环 sta 来计算相对于 lon_orig/lat_orig 的距离。对于实际绘图(使用contourf),我们会将数组展平为一维数组。这一切似乎有点乏味,我想知道我是否错过了这样做的明显方法?

最终,我们需要三个具有相同形状的一维数组进行绘图:

  • z(对于这个数据集:时间)
  • 距离(相对于 x0/y0,此处根据经度和纬度计算)
  • 实际数据(此处:气温)

谢谢!

【问题讨论】:

    标签: arrays numpy python-xarray


    【解决方案1】:

    设置我的示例

    首先,我将使用一个不同的示例,因为我想知道我应该得到什么结果。我的示例是一个 100x100x100 的数组,其中包含一个半径为 50 的球。

    我采用了this answer 的代码来绘制它。

    n = 100
    
    def distance_from_center(x,y,z):
        V = np.stack([x-n/2,y-n/2,z-n/2])
        return np.linalg.norm(V, axis=0)
    
    ball = np.fromfunction(distance_from_center, (n, n, n), dtype='float')
    ball = (ball > 50).astype('int')
    

    基本解决方案

    你可以用 4 行代码来做这样的事情。所以在我变得更花哨之前,我会先这样做。我的第一片是从一个角落到另一个角落。像这样:

    我已经用绿色标记了我要切片的位置。

    t = np.arange(n)
    slice = ball[t,t,:]
    plt.matshow(slice.T, cmap='gray')
    plt.gca().set_aspect(1/2**(1/2))
    

    请注意,我必须将纵横比设置为 2 的平方根的 1,因为对角线方向上的 50 个像素比普通方向上的 50 个像素要长。图片看起来像预期的那样。两边都有空白,因为数组的角为其提供了空间,但我们仍然得到一个圆圈。

    您可以像这样进行其他切割,但您总是在考虑如何获得整数坐标。

    更复杂的解决方案

    另一种方法是只取任何坐标并在您不能完美命中的点之间进行插值。

    import itertools
    
    class Image_knn():
        def fit(self, image):
            self.image = image.astype('float')
    
        def predict(self, x, y, z):
            image = self.image
            weights_x = [(1-(x % 1)).reshape(-1), (x % 1).reshape(-1)]
            weights_y = [(1-(y % 1)).reshape(-1), (y % 1).reshape(-1)]
            weights_z = [(1-(z % 1)).reshape(-1), (z % 1).reshape(-1)]
            start_x = np.floor(x)
            start_y = np.floor(y)
            start_z = np.floor(z)
    
            return sum([image[np.clip(np.floor(start_x + x), 0, image.shape[0]-1).astype('int'),
                              np.clip(np.floor(start_y + y), 0, image.shape[1]-1).astype('int'),
                              np.clip(np.floor(start_z + z), 0, image.shape[1]-1).astype('int')]
                        *weights_x[x]*weights_y[y]*weights_z[z]
                        for x,y,z in itertools.product(range(2),range(2),range(2))])
    
        
    image_model = Image_knn()
    image_model.fit(ball)
    
    fig, ax = plt.subplots(nrows=3,ncols=3)
    ax = ax.reshape(-1)
    

    我将给出一个使用不同切片的示例,其中一个方向是 z 方向,另一个是包含左下角和右下角不同点的方向。首先是指示切割位置的 3d 图:

    现在我计算切割的坐标并绘制它们。这次我可以保持纵横比,因为我在更长的方向上使用了更多的点。请注意,我将第二个点的坐标放在绘图的标题中。

    fig, ax = plt.subplots(nrows=3,ncols=3)
    ax = ax.reshape(-1)
    
    for i,x in enumerate(np.linspace(0,100,9)):
        p = np.array([0,100])
        q = np.array([100,100-x])
        distance = np.round(np.linalg.norm(q-p)).astype('int')
        t = np.linspace(0,1,distance)
        xy = t.reshape(-1,1)*q+(1-t).reshape(-1,1)*p
        r,s = np.meshgrid(np.arange(100), np.arange(distance))
        x = xy[s][:,:,0].reshape(-1)
        y = xy[s][:,:,1].reshape(-1)
        z = r.reshape(-1)
    
        out = image_model.predict(x,y,z)
        ax[i].imshow(out.reshape(distance, 100).T, cmap='gray',vmin=0,vmax=1)
        ax[i].set_title(tuple(q.astype('int')))
    

    【讨论】:

      猜你喜欢
      • 2018-07-18
      • 2020-10-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多