将值与 numpy 中的相邻元素进行比较

Art*_*hur 5 python numpy scipy

假设我有一个 numpy 数组

    a b c
A = i j k
    u v w
Run Code Online (Sandbox Code Playgroud)

我想将值中心元素与其八个相邻元素中的一些元素(沿轴或沿对角线)进行比较。除了嵌套的 for 循环(对于大矩阵来说太慢)之外,还有什么更快的方法吗?

更具体地说,我想做的是将元素的值与其邻居进行比较并分配新值。

例如:

    if (j == 1):
      if (j>i) & (j>k):
        j = 999
      else:
        j = 0
    if (j == 2):
      if (j>c) & (j>u):
        j = 999
      else:
        j = 0
   ...
Run Code Online (Sandbox Code Playgroud)

像这样的东西。

use*_*160 5

您的操作包含许多条件,因此在一般情况下(任何类型的条件、任何类型的操作)执行此操作的最有效方法是使用循环。这可以使用 numba 或 cython 有效地完成。在特殊情况下,您可以使用 numpy/scipy 中的更高级别的函数来实现它。我将为您提供的特定示例展示一个解决方案,希望您可以从那里进行概括。

从一些假数据开始:

A = np.asarray([
    [1, 1, 1, 2, 0],
    [1, 0, 2, 2, 2],
    [0, 2, 0, 1, 0],
    [1, 2, 2, 1, 0],
    [2, 1, 1, 1, 2]
])
Run Code Online (Sandbox Code Playgroud)

我们将找到A适用各种条件的位置。

  • 1a) 值为 1
  • 1b) 该值大于其水平邻居
  • 2a) 值为 2
  • 2b) 该值大于其对角线邻居

查找A出现指定值的位置:

cond1a = A == 1
cond2a = A == 2
Run Code Online (Sandbox Code Playgroud)

这给出了大小与 相同的布尔值矩阵A。条件成立时该值为真,否则为假。

查找A每个元素与其邻居具有指定关系的位置:

# condition 1b: value greater than horizontal neighbors
f1 = np.asarray([[1, 0, 1]])
cond1b = A > scipy.ndimage.maximum_filter(
    A, footprint=f1, mode='constant', cval=-np.inf)

# condition 2b: value greater than diagonal neighbors
f2 = np.asarray([
    [0, 0, 1],
    [0, 0, 0],
    [1, 0, 0]
])
cond2b = A > scipy.ndimage.maximum_filter(
    A, footprint=f2, mode='constant', cval=-np.inf)
Run Code Online (Sandbox Code Playgroud)

和以前一样,这给出了指示条件为真的布尔值矩阵。此代码使用scipy.ndimage.maximum_filter()。此函数迭代地将“足迹”移动到以 的每个元素为中心A。该位置的返回值是足迹为 1 的所有元素的最大值。该mode参数指定如何处理矩阵边界外的隐式值,其中足迹从边缘落下。在这里,我们将它们视为负无穷大,这与忽略它们相同(因为我们使用的是最大操作)。

根据条件设置结果的值。如果条件 1a 和 1b 都为真,或者条件 2a 和 2b 都为真,则值为 999。否则,值为 0。

result = np.zeros(A.shape)
result[(cond1a & cond1b) | (cond2a & cond2b)] = 999
Run Code Online (Sandbox Code Playgroud)

结果是:

[
    [  0,   0,   0,   0,   0],
    [999,   0,   0, 999, 999],
    [  0,   0,   0, 999,   0],
    [  0,   0, 999,   0,   0],
    [  0,   0,   0,   0, 999]
]
Run Code Online (Sandbox Code Playgroud)

您可以通过更改过滤器覆盖区将此方法推广到其他邻居模式。您可以使用其他类型的过滤器(请参阅scipy.ndimage)推广到其他操作(最小值、中值、百分位数等)。对于可以表示为加权和的运算,请使用2d 互相关

这种方法应该比在 python 中循环快得多。但是,它确实执行了不必要的计算(例如,只有当值为 1 或 2 时才需要计算最大值,但我们对所有元素都这样做)。手动循环可以避免这些计算。在 python 中循环可能比这里的代码慢得多。但是,在 numba 或 cython 中实现它可能会更快,因为这些工具会生成编译代码。