1

我需要沿数组(1)的时间轴的平均值。

问题:它不会是沿该轴的所有值的平均值,而是从数组 (2) 中给定的索引开始的子集。

我正在使用的数组:

 (array1) 3 axes: time, x, y
array([[[ 820,  820,  720,  720],
        [ 860,  860,  500,  500],
        [ 860,  860,  500,  500],
        [ 860,  860,  500,  500]],
       [[5980, 5980, 4760, 4760],
        [7500, 7500, 7940, 7940],
        [7500, 7500, 7940, 7940],
        [7500, 7500, 7940, 7940]],
       [[ 740,  740,  440,  440],
        [1240, 1240, 1140, 1140],
        [1240, 1240, 1140, 1140],
        [1240, 1240, 1140, 1140]],
       [[3200, 3200, 7600, 7600],
        [ 900,  900,  400,  400],
        [ 900,  900,  400,  400],
        [ 900,  900,  400,  400]]])
 (array2) 2 axes: x, y 
array([[  1,   2,   1,   1],
       [  1,   0,   3,   3],
       [  4,   0,   2,   2],
       [  4,   0,   1,   2]])

为了进一步说明这个例子:

array1 中的值表示位置 x/y 每天的降雨量。array2 中的值表示从哪一天开始需要计算位置 x/y 的平均值。

查看第一个单元格,我们将从计算中排除第一天,因为 array2[0,0] = 1。所以我们的结果将是 np.mean(array1[1:, 0, 0]) = 3306.67。

我无法理解的是如何根据数组 2 为每个单元格指定子集。我知道我可以沿任何轴使用 np.mean,但是如何从计算中动态排除值(切片数组) ?

4

2 回答 2

2
arr1 = np.array(
    [[[ 820,  820,  720,  720],
      [ 860,  860,  500,  500],
      [ 860,  860,  500,  500],
      [ 860,  860,  500,  500]],
     
     [[5980, 5980, 4760, 4760],
      [7500, 7500, 7940, 7940],
      [7500, 7500, 7940, 7940],
      [7500, 7500, 7940, 7940]],
     
     [[ 740,  740,  440,  440],
      [1240, 1240, 1140, 1140],
      [1240, 1240, 1140, 1140],
      [1240, 1240, 1140, 1140]],
     
     [[3200, 3200, 7600, 7600],
      [ 900,  900,  400,  400],
      [ 900,  900,  400,  400],
      [ 900,  900,  400,  400]]]
)
arr2 = np.array(
    [[  1,   2,   1,   1],
     [  1,   0,   3,   3],
     [  3,   0,   2,   2],
     [  3,   0,   1,   2]]
)

我们要做的是arr1使用存储在 中的索引对时间轴进行arr2切片,现在 python 只允许切片:,我们只能在按字面意思进行索引时传递,即不使用另一个数组进行索引。所以我们需要一个回合来做这件事

一种方法可能是将 中的所有值更改为arr1如果切片将被忽略0

现在要找到要忽略的值的索引,我们这样做

no_days = arr1.shape[0]
arr3 = np.arange(no_days)
arr3.shape = [-1,1,1]
arr3

>>> [[[0]],

     [[1]],

     [[2]],

     [[3]]]
filter = arr3 < arr2
filter.shape

>>> (4, 4, 4)

arr3是时间轴索引的数组。我们将它与它进行了比较arr2,现在我们有了要忽略的布尔值索引,filter我们可以将它们设置为0

arr1[filter] = 0
arr1

>>>   [[[   0,    0,    0,    0],
        [   0,  860,    0,    0],
        [   0,  860,    0,    0],
        [   0,  860,    0,    0]],

       [[5980,    0, 4760, 4760],
        [7500, 7500,    0,    0],
        [   0, 7500,    0,    0],
        [   0, 7500, 7940,    0]],

       [[ 740,  740,  440,  440],
        [1240, 1240,    0,    0],
        [   0, 1240, 1140, 1140],
        [   0, 1240, 1140, 1140]],

       [[3200, 3200, 7600, 7600],
        [ 900,  900,  400,  400],
        [ 900,  900,  400,  400],
        [ 900,  900,  400,  400]]]

我们可能很想使用arr1.mean(axis= 0),但这样做也会考虑所有0影响均值的合法条目,而不是忽略它们

所以相反,我们arr1对时间轴求和,并将其除以切片中的元素

arr1.sum(axis= 0) / (no_days - arr2)

>>>   [[3306.66666667, 1970.        , 4266.66666667, 4266.66666667],
       [3213.33333333, 2625.        ,  400.        ,  400.        ],
       [ 900.        , 2625.        ,  770.        ,  770.        ],
       [ 900.        , 2625.        , 3160.        ,  770.        ]]

如果t < x*y那么以下将执行得更快

arr1.sum(axis= 0) / (~filter).astype(int).sum(axis= 0)

于 2020-07-09T22:29:33.987 回答
1

我找到了一种使用xarray的方法。它不是很漂亮,但至少我相信它是矢量化的。

首先,将 numpy 数组转换为 xarray DataArray,并Dataset使用xr.merge

rainfall = xr.DataArray(rainfall, dims=("day", "x", "y"), name="rainfall")
start_idxs = xr.DataArray(start_idxs, dims=("x", "y"), name="start_idxs")

ds = xr.merge((rainfall, start_idxs))

这里 a 的Dataset样子:

>>> ds
<xarray.Dataset>
Dimensions:     (day: 4, x: 4, y: 4)
Dimensions without coordinates: day, x, y
Data variables:
    rainfall    (day, x, y) int64 820 820 720 720 860 ... 400 900 900 400 400
    start_idxs  (x, y) int64 1 2 1 1 1 0 3 3 4 0 2 2 4 0 1 2

然后我们想根据start_idxs变量进行不同的计算,所以我们将groupby这个数据变量:

>>> groups = ds.groupby("start_idxs")
>>> groups
DatasetGroupBy, grouped over 'start_idxs' 
5 groups with labels 0, 1, 2, 3, 4.

您会看到如预期的那样有 5 个组。现在我们要为每个组应用一个计算,所以我们将使用map.

>>> res = groups.map(mean_start_idxs, args=("day",))
>>> res["rainfall"]
<xarray.DataArray 'rainfall' (x: 4, y: 4)>
array([[3306.66666667, 1970.        , 4266.66666667, 4266.66666667],
       [3213.33333333, 2625.        ,  400.        ,  400.        ],
       [          nan, 2625.        ,  770.        ,  770.        ],
       [          nan, 2625.        , 3160.        ,  770.        ]])
Dimensions without coordinates: x, y

这是预期的输出。注意nan要求从索引 4 开始平均值的值,只有 4 天这是不可能的。

但要让它工作,我们需要定义mean_start_idxs函数,这是棘手的部分。

这很棘手,因为从调用的函数内部检索组的“标签”并不简单map,但这里有一个解决方案:

def mean_start_idxs(ds, dim):
    # Get the start indice
    #   groups were made from start_idxs, so we can
    #   take any value of ds["start_idxs"] as a start indice
    start = ds["start_idxs"][0].item()
    end = ds.sizes[dim]

    return ds.isel({dim: slice(start, end)}).mean(dim=dim)
于 2020-07-09T21:17:54.140 回答