如何将长度为1的xarray DataArray与较大的数组对齐



我想取一个时间维度为1的xarray数据集,只需复制数据即可将时间维度从1增加到N。最有效的方法是什么?我已经尝试了几种方法,比如expand_dims和stack,但似乎都不能达到我想要的效果。

最终我希望能够moc10_H11-moc_ctrl_clim其中结果将具有与moc10_H11(35(相同的维度。现在,当我这样做的时候,输出的时间维度只有1。

为了清晰起见,moc_ctrl_clim:

Dimensions:
time: 1, lat_aux_grid: 395, moc_z: 61
Coordinates: time (time) object 0001-01-01 00:00:00
lat_aux_grid (lat_aux_grid) float32 -79.49 -78.95 -78.42 ... 89.47 90.0
moc_z (moc_z) float32 0.0 1e+03 ... 5.25e+05 5.5e+05
Data variables:
MOC (time, moc_z, lat_aux_grid) float64
dask.array<chunksize=(1, 61, 395), meta=np.ndarray>

moc10_H11具有:

Dimensions:
time: 35, lat_aux_grid: 395, moc_z: 61
Coordinates: time (time) object 0001-01-01 00:00:00
lat_aux_grid (lat_aux_grid) float32 -79.49 -78.95 -78.42 ... 89.47 90.0
moc_z (moc_z) float32 0.0 1e+03 ... 5.25e+05 5.5e+05
Data variables:
MOC (time, moc_z, lat_aux_grid) float64
dask.array<chunksize=(1, 61, 395), meta=np.ndarray>

简短的回答,压缩数据,使xarray的自动对齐规则生效:

da = da.squeeze(dim='time', drop=True)

现在,您可以与一个按时间索引的数组配对,数据将自动广播。

解释

这背后的原因在于numpy的基于形状的广播和xarray的基于维度名称的广播之间的差异。

按形状排列的麻木广播

来自numpy文档:

当对两个数组进行操作时,NumPy会按元素比较它们的形状。它从尾部(即最右侧(尺寸开始,然后向左移动。时两个维度兼容

  1. 它们是相等的,或者
  2. 其中一个是1

例如,如果第一个维度对齐,则可以在列向量和数组之间执行元素相加:

In [3]: col_vector = np.ones(shape=(3, 1))
In [4]: col_vector
Out[4]:
array([[1.],
[1.],
[1.]])
In [5]: array = np.arange(12).reshape(3, 4)
In [6]: array
Out[6]:
array([[ 0,  1,  2,  3],
[ 4,  5,  6,  7],
[ 8,  9, 10, 11]])
In [7]: col_vector + array
Out[7]:
array([[ 1.,  2.,  3.,  4.],
[ 5.,  6.,  7.,  8.],
[ 9., 10., 11., 12.]])

col_vector被添加到array时,numpy识别出col_vector沿着轴1具有长度1,并且array具有长度4,因此col_vector在被添加之前应该沿着轴1被广播(平铺(为具有长度4。

按维度名称进行xarray广播

从xarray文档计算:

DataArray对象会根据维度名称而不是轴顺序自动对齐(用numpy的说法是"广播"(。使用xarray,您不需要转置数组或插入长度为1的维度来进行数组操作,就像通常在numpy中使用numpy.reshape()numpy.newaxis所做的那样。

除此之外,在xarray文档中自动对齐:

Xarray强制二进制操作中使用的对象的索引坐标(即与维度同名的坐标,用*标记(之间的对齐。[…]如果任一参数中缺少维度的坐标值,则所有匹配的维度都必须具有相同的大小。

适应上面的例子不仅需要分配名称和坐标维度,而且还需要从列向量中删除第二个维度

In [2]: vector = xr.DataArray(np.ones(shape=3), dims=['x'], coords=[[0, 1, 2]])
In [3]: vector
Out[3]:
<xarray.DataArray (x: 3)>
array([1., 1., 1.])
Coordinates:
* x        (x) int64 0 1 2
In [4]: arr = xr.DataArray(
...:     np.arange(12).reshape(3, 4),
...:     dims=['x', 'time'],
...:     coords=[[0, 1, 2], pd.date_range('2020-01-01', periods=4, freq='D')],
...: )
In [5]: arr
Out[5]:
<xarray.DataArray (x: 3, time: 4)>
array([[ 0,  1,  2,  3],
[ 4,  5,  6,  7],
[ 8,  9, 10, 11]])
Coordinates:
* x        (x) int64 0 1 2
* time     (time) datetime64[ns] 2020-01-01 2020-01-02 2020-01-03 2020-01-04
In [6]: vector + arr
Out[6]:
<xarray.DataArray (x: 3, time: 4)>
array([[ 1.,  2.,  3.,  4.],
[ 5.,  6.,  7.,  8.],
[ 9., 10., 11., 12.]])
Coordinates:
* x        (x) int64 0 1 2
* time     (time) datetime64[ns] 2020-01-01 2020-01-02 2020-01-03 2020-01-04

广播长度-1维度到更长维度

在您的问题中,您有一个沿时间维度长度为1的数组,您希望对另一个具有较长时间坐标的数组进行广播。这在上面的例子中相当于具有一个";矢量";时间维度中的长度为1:

In [7]: vector = xr.DataArray(
...:     np.ones(shape=(3, 1)),
...:     dims=['x', 'time'],
...:     coords=[[0, 1, 2], pd.date_range('2020-01-01', periods=1, freq='D')],
...: )

当针对具有长度为4的时间维度的arr广播此消息时,仅保留交集:

In [8]: vector + arr
Out[8]:
<xarray.DataArray (x: 3, time: 1)>
array([[1.],
[5.],
[9.]])
Coordinates:
* time     (time) datetime64[ns] 2020-01-01
* x        (x) int64 0 1 2

通过首先用da.squeeze:压缩和丢弃时间调光,可以实时广播数据

In [9]: vector.squeeze('time', drop=True)  + arr
Out[9]:
<xarray.DataArray (x: 3, time: 4)>
array([[ 1.,  2.,  3.,  4.],
[ 5.,  6.,  7.,  8.],
[ 9., 10., 11., 12.]])
Coordinates:
* x        (x) int64 0 1 2
* time     (time) datetime64[ns] 2020-01-01 2020-01-02 2020-01-03 2020-01-04

请注意,此方法忽略time坐标中第一个数组中的信息,而假设该信息适用于第二个数组中time的所有元素。如果这就是你想要的,那么挤压&如图所示,下降是可行的。

最新更新