了解 xarray.apply_ufunc

Understanding xarray.apply_ufunc

我有一个具有多个时间维度的 xarray slow_timefast_time 一个表示不同对象的维度 object 和一个反映每个对象在每个时间点的位置的维度 coords.

现在的目标是使用 scipy.spatial.transform.Rotation 对每个时间点的数组中的每个位置应用旋转。

我正在努力弄清楚如何使用 xarray.apply_ufunc 来做我想做的事,主要是因为 input_core_dimensions 的概念对我来说不是很清楚。

下面的代码显示了我正在尝试做的事情:

import numpy as np
import xarray as xr
from scipy.spatial.transform import Rotation

# dummy initial positions
initial_position = xr.DataArray(np.arange(6).reshape((-1,3)), dims=["object", "coords"])
# dummy velocities
velocity = xr.DataArray(np.array([[1, 0, 0], [0, 0.5, 0]]), dims=["object", "coords"])

slow_time = xr.DataArray(np.linspace(0, 1, 10, endpoint=False), dims=["slow_time"])
fast_time = xr.DataArray(np.linspace(0, 0.1, 100, endpoint=False), dims=["fast_time"])

# times where to evaluate my function
times = slow_time + fast_time

# this is the data I want to transform
positions = times * velocity + initial_position

# these are the rotation angles
theta = np.pi/20 * times
phi = np.pi/100 * times

def apply_rotation(vectors, theta, phi):
    R = Rotation.from_euler("xz", (theta, phi))
    result = R.apply(vectors)
    return result

rotated_positions = xr.apply_ufunc(apply_rotation,
    positions, theta, phi, ...??)

我正在寻找的行为本质上就像四个嵌套的 for 循环,像这个伪代码一样将旋转应用于每个点

for pos, t, p in zip(positions, theta, phi):
    R = Rotation.from_euler("xz", (t, p))
    R.apply(pos)

但我不确定如何进行。

使用这个

rotated_positions = xr.apply_ufunc(apply_rotation,
    positions, theta, phi,
    input_core_dims=[["object"],[],[]], output_core_dims=[["object"]])

我原以为该函数将沿 object 维度的子数组应用,但现在我将整个数组传递到我的函数中,但它不起作用。

xarray 文档中关于 apply_ufunc 的信息并不是很清楚。

感谢任何帮助!

参考

首先,一个有用的参考是 this documentation page on unvectorized ufunc

解决方案

据我了解你的问题,你想每次都对每个对象的位置向量应用旋转。 您设置数据的方式已经将坐标作为数组的最终维度。

翻译您的伪代码以生成参考数据集 rotatedPositions 产量:

rotatedPositions = positions.copy()
for slowTimeIdx in range( len(slow_time)):
    for fastTimeIdx in range( len(fast_time) ):
        for obj in range(2):
            pos = rotatedPositions.data[slowTimeIdx, fastTimeIdx, obj].copy()
            rotatedPositions.data[slowTimeIdx, fastTimeIdx, obj] =  apply_rotation(pos, theta.data[slowTimeIdx, fastTimeIdx], phi.data[slowTimeIdx, fastTimeIdx])

其中我 hard-coded object 维度大小。

本质上,apply_rotation 函数采用 1 个 3 向量(大小为 3 的一维数组)和 2 个标量,returns 大小为 3 的一维数组(向量为 3)。

根据上面提到的文档,我得到了以下对 apply_ufunc 的调用:

rotated = xr.apply_ufunc(apply_rotation, 
               positions,
                theta,
                phi,
               input_core_dims=[['coords'], [], []],
               output_core_dims=[['coords']],
               vectorize=True
              )

测试通过

np.allclose(rotatedPositions.data, rotated.data)

表示成功。

说明

据我了解,上面引用的文档 apply_ufunc 会将要应用的函数作为第一个参数,然后依次是所有位置参数。

下一个必须提供每个数据集的维度标签,这些标签将对应于 coreapply_rotation 工作的数据。这是 coords,因为我们操纵坐标。由于 thetaphi 都没有这个维度,我们没有为它们指定任何内容。

接下来我们必须指定输出数据的维度,因为我们只是转换我们保留的输出数据 output_core_dims=[['coords']]。忽略它会导致 apply_ufunc 假设输出数据为 0 维(标量)。

最后,vectorize=True 确保函数在 而非 中指定的所有维度上执行 input_core_dims