我正在尝试使用xarray apply_ufunc
将给定的函数f
应用于数据集中的所有坐标对(即像素)。
函数f
返回2D数组(NxN矩阵)作为结果。因此,分析后所得的数据集将具有几个新变量:总共M
个新变量。
f
功能确实可以正常工作。因此,该错误似乎并非源于此。
[可能的问题可能是2D数组从f
返回的结构。据我了解,xarray.apply_ufunc
要求结果数组以元组结构化。因此,我什至尝试将2D数组转换为数组的元组,但到目前为止没有任何效果。
也可以在其他作品works的其他地方检查此情况。在此当前链接中,作者必须对原始数据集运行两次相同的线性回归拟合函数,以便从回归中检索所有参数(beta_0和alpha)。
因此,我想知道,xarray.apply_ufunc
是否能够像上面的链接(或下面的代码片段中的代码)一样操作归约函数,它返回多个新变量。
下面,我将介绍涉及所讨论问题的可复制代码。请注意,函数f
返回2D数组。第二维的深度为4。因此,在整个处理之后,我希望有一个包含4个新变量的结果数据集。
import numpy as np
import xarray as xr
x_size = 10
y_size = 10
time_size = 30
lon = np.arange(50, 50+x_size)
lat = np.arange(10, 10+y_size)
time = np.arange(10, 10+time_size)
array = np.random.randn(y_size, x_size, time_size)
ds = xr.DataArray(
data=array,
coords = {'lon':lon, 'lat':lat, 'time':time},
dims=('lon', 'lat', 'time')
)
def f (x):
return (x, x**2, x**3, x**4)
def f_xarray(ds, dim=['time'], dask='allowed', new_dim_name=['predicted']):
filtered = xr.apply_ufunc(
f,
ds,
dask=dask,
vectorize=True,
input_core_dims=[dim],
#exclude_dims = dim, # This must not be setted.
output_core_dims= [['x', 'x2', 'x3', 'x4']], #[new_dim_name],
#kwargs=kwargs,
#output_dtypes=[float],
#dataset_join='outer',
#dataset_fill_value=np.nan,
).compute()
return filtered
ds2 = f_xarray(ds)
# Error message returned:
# ValueError: wrong number of outputs from pyfunc: expected 1, got 4
很难熟悉xarray.apply_ufunc
,它提供了非常广泛的可能性,而且并不总是很清楚如何最大程度地利用它。在这种情况下,错误是由于input_core_dims
和output_core_dims
引起的。我将首先扩展他们的文档,重点介绍我认为引起混乱的原因,然后提供一些解决方案。他们的文档是:
与args长度相同的列表,给出每个广播输入参数上不应广播的核心尺寸列表。默认情况下,我们假定任何输入参数上都没有核心尺寸。
例如,input_core_dims = [[],['time']]指示应广播第一个参数的所有尺寸以及第二个参数的“时间”以外的所有尺寸。
在应用func之前,核心尺寸会自动移动到输入变量的最后一个轴,这有助于使用NumPy样式的通用ufuncs [2]。
它负责计算的两个重要方面。首先,它定义了要广播的尺寸,这特别重要,因为假定输出的形状与这些广播的尺寸所定义的形状相同(如果不是这种情况,则必须使用output_core_dims
)。其次,input_core_dims
移至末尾。下面有两个示例:
我们可以在不对apply_ufunc
附加任何参数的情况下应用不会修改形状的函数:
xr.apply_ufunc(lambda x: x**2, ds)
# Output
<xarray.DataArray (lon: 10, lat: 10, time: 30)>
array([[[6.20066642e+00, 1.68502086e+00, 9.77868899e-01, ...,
...,
2.28979668e+00, 1.76491683e+00, 2.17085164e+00]]])
Coordinates:
* lon (lon) int64 50 51 52 53 54 55 56 57 58 59
* lat (lat) int64 10 11 12 13 14 15 16 17 18 19
* time (time) int64 10 11 12 13 14 15 16 17 18 ... 32 33 34 35 36 37 38 39
例如,要计算沿lon
维的平均值,我们减小其中一个维,因此,输出将比输入小一维:我们必须将lon
传递为input_core_dim
:
xr.apply_ufunc(lambda x: x.mean(axis=-1), ds, input_core_dims=[["lon"]])
# Output
<xarray.DataArray (lat: 10, time: 30)>
array([[ 7.72163214e-01, 3.98689228e-01, 9.36398702e-03,
...,
-3.70034281e-01, -4.57979868e-01, 1.29770762e-01]])
Coordinates:
* lat (lat) int64 10 11 12 13 14 15 16 17 18 19
* time (time) int64 10 11 12 13 14 15 16 17 18 ... 32 33 34 35 36 37 38 39
请注意,即使axis=-1
是第一个维度,我们仍在计算lon
的均值,因为它是input_core_dims
时将被移到最后。因此,我们可以使用lat
计算沿input_core_dims=[["lon"]]
暗淡的平均值。
还要注意input_core_dims
的格式,必须是列表列表:与args长度相同的列表给出了核心尺寸列表。元组(或任何序列)的元组也是有效的,但是,请注意,对于元组,元素为1的情况是(("lon",),)
而不是(("lon"))
。
与func的输出参数数量相同长度的列表,给出每个输出中未在输入中广播的核心尺寸的列表。默认情况下,我们假定func输出的数组恰好是一个数组,其轴对应于每个广播维度。
假定核心尺寸按提供的顺序显示为每个输出的最后一个尺寸。
再次,output_core_dims
是列表列表。当有多个输出时(即func返回一个元组),或者输出除了广播的尺寸外还具有其他尺寸时,必须使用它。显然,如果有多个带有额外暗淡的输出,则也必须使用它。我们将使用两种可能的解决方案作为示例。
使用问题中发布的功能。此函数返回一个元组,因此即使数组的形状未修改,我们也需要使用output_core_dims
。由于实际上没有多余的暗淡,我们将为每个输出传递一个空列表:
xr.apply_ufunc(
f,
ds,
output_core_dims= [[] for _ in range(4)],
)
这将返回一个DataArrays元组,其输出将与f(ds)
完全相同。
我们现在将修改函数以输出单个数组,将所有4个输出堆叠在元组中。注意,我们必须确保在数组末尾添加了这个新维度:
def f2(x):
return np.stack((x, x**2, x**3, x**4), axis=-1)
xr.apply_ufunc(
f2,
ds,
output_core_dims= [["predictions"]],
)
# Output
<xarray.DataArray (lon: 10, lat: 10, time: 30, predictions: 4)>
array([[[[ 2.49011374e+00, 6.20066642e+00, 1.54403646e+01,
...,
4.71259686e+00]]]])
Coordinates:
* lon (lon) int64 50 51 52 53 54 55 56 57 58 59
* lat (lat) int64 10 11 12 13 14 15 16 17 18 19
* time (time) int64 10 11 12 13 14 15 16 17 18 ... 32 33 34 35 36 37 38 39
Dimensions without coordinates: predictions
我们现在已将predictions
作为输出核心昏暗传递,这使输出除原始3之外还具有predictions
作为新尺寸。这里的输出不再等于f2(ds)
(它返回一个numpy数组)。因为有了apply_ufunc
的帮助,我们才能够执行一些功能和堆叠而不会丢失标签。
旁注:通常不建议将可变对象用作函数中的默认参数:例如,请参见"Least Astonishment" and the Mutable Default Argument