【问题标题】:Masking a Numpy array and applying a calculation per mask without using a for loop屏蔽 Numpy 数组并在不使用 for 循环的情况下对每个掩码应用计算
【发布时间】:2019-02-15 12:18:35
【问题描述】:

假设我们有以下数据数组:

data_array = np.array([[1, 1, 1], [1, 1, 2], [2, 2, 2], [3, 3, 3], [4, 4, 4]], np.int16)

data_array
array([[1, 1, 1],
       [1, 1, 2],
       [2, 2, 2],
       [3, 3, 3],
       [4, 4, 4]])

我们希望根据以下范围屏蔽数组,以便能够对屏蔽部分应用计算:

intervals = [[1, 2], [2, 3], [3, 4]]

我们首先根据数据数组创建一个空数组和掩码,这样我们就可以组合每个掩码数组的结果:

init = np.zeros((data_array.shape[0], data_array.shape[1]))
result_array = np.ma.masked_where((init == 0), init)

result_array
masked_array(
data=[[--, --, --],
      [--, --, --],
      [--, --, --],
      [--, --, --],
      [--, --, --]],
mask=[[ True,  True,  True],
      [ True,  True,  True],
      [ True,  True,  True],
      [ True,  True,  True],
      [ True,  True,  True]]

这样我们可以启动一个for循环,根据区间范围屏蔽数组,对屏蔽数组执行计算并将结果组合到单个结果数组:

for inter in intervals:

    # Extact the start and en values for interval range
    start_inter = inter[0]
    end_inter = inter[1]

    # Mask the array based on interval range
    mask_init = np.ma.masked_where((data_array > end_inter), data_array)
    masked_array = np.ma.masked_where((mask_init < start_inter), mask_init)

    # Perform a dummy calculation on masked array
    outcome = (masked_array + end_inter) * 100

    # Combine the outcome arrays
    result_array[result_array.mask] = outcome[result_array.mask]

结果如下:

array([[300.0, 300.0, 300.0],
      [300.0, 300.0, 400.0],
      [400.0, 400.0, 400.0],
      [600.0, 600.0, 600.0],
      [800.0, 800.0, 800.0]])

我的问题是,如果不使用这个 for 循环,如何实现相同的结果?因此,在单个操作中对整个 data_array 应用掩码和计算。请注意,计算的变量随每个掩码而变化。是否可以对这个问题应用矢量化方法?我想numpy_indexed 可能会有所帮助。谢谢你。

【问题讨论】:

  • 所以您需要data_array 上的每个值仅受包含它的第一个间隔的影响?也就是说,例如,在这种情况下,2 位于第一个和第二个区间内(因为在定义它们时,区间两端都包含),但每个 2 都会转换为 400,因为只有考虑第一个间隔(否则我想你会通过添加第一个和第二个间隔的结果得到900)。这是一个要求吗?
  • 好点。这确实是一个要求,因此如果值在多个掩码中(在本例中为 2),我们不会添加结果。我们只使用最后一个区间的结果(对于2,这确实是400)。在实际用例中,我们可以使用非重叠间隔,对于这个例子,我们可以使用intervals = [[1, 1.9], [2, 2.9], [3, 4]]

标签: python arrays numpy for-loop numpy-indexed


【解决方案1】:

如果可以使间隔不重叠,那么您可以使用如下函数:

import numpy as np

def func(data_array, intervals):
    data_array = np.asarray(data_array)
    start, end = np.asarray(intervals).T
    data_array_exp = data_array[..., np.newaxis]
    mask = (data_array_exp >= start) & (data_array_exp <= end)
    return np.sum((data_array_exp + end) * mask * 100, axis=-1)

在这种情况下,结果应该与原始代码相同:

import numpy as np

def func_orig(data_array, intervals):
    init = np.zeros((data_array.shape[0], data_array.shape[1]))
    result_array = np.ma.masked_where((init == 0), init)
    for inter in intervals:
        start_inter = inter[0]
        end_inter = inter[1]
        mask_init = np.ma.masked_where((data_array > end_inter), data_array)
        masked_array = np.ma.masked_where((mask_init < start_inter), mask_init)
        outcome = (masked_array + end_inter) * 100
        result_array[result_array.mask] = outcome[result_array.mask]
    return result_array.data

data_array = np.array([[1, 1, 1], [1, 1, 2], [2, 2, 2], [3, 3, 3], [4, 4, 4]], np.int16)
intervals = [[1, 1.9], [2, 2.9], [3, 4]]
print(np.allclose(func(data_array, intervals), func_orig(data_array, intervals)))
# True

【讨论】:

  • 做了一些彻底的检查,效果很好。对于较大的数组,它可能会占用大量内存,但对于小数组,它是完美的。
猜你喜欢
  • 1970-01-01
  • 2021-01-29
  • 1970-01-01
  • 2021-11-01
  • 2018-10-15
  • 1970-01-01
  • 2017-03-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多