首页 > 解决方案 > 计算具有相同值的numpy数组中值的(部分)邻居

问题描述

我正在尝试为 numpy 数组中的每个条目计算/查找具有相同值的邻居的分数。它需要高性能(在更大的阵列上运行多次),可能具有更改邻域定义的能力(车/皇后,即 4 个邻居:NSEW,与 8 个邻居:NE、N、NW、E、W、SE ,S,SW),并且可以将 0 设置为 NaN 或不设置。

考虑一个简单的整数 numpy 数组:

a = np.zeros((6,6), dtype=np.int64)
a[1,2] = a[1,3] = 1
a[4,4] = a[5,4] = a[5,5] = 3
a[4,3] = 2

看起来像:

[[0 0 0 0 0 0]
[0 0 1 1 0 0]
[0 0 0 0 0 0]
[0 0 0 0 0 0]
[0 0 0 2 3 0]
[0 0 0 0 3 3]]

我想生成以下数组,我们认为 0 被屏蔽掉(不是数字):

[[0 0 0 0 0 0]
[0 0 1 1 0 0]
[0 0 0 0 0 0]
[0 0 0 0 0 0]
[0 0 0 0 2 0]
[0 0 0 0 2 2]]

或者更好,如果表示为分数:

[[0 0 0 0 0 0]
[0 0 1/8 1/8 0 0]
[0 0 0 0 0 0]
[0 0 0 0 0 0]
[0 0 0 0 2/8 0]
[0 0 0 0 2/5 2/3]]

我已经考虑了与 scipy 卷积的相似之处,除了它们会带来总和,而不是计数。

标签: pythonnumpyscipyconvolution

解决方案


卷积正是您正在寻找的。例如,scipy.signal.convolve。考虑以下内核:

kernel = np.ones((3, 3), dtype=int)
kernel[1, 1] = 0
# array([[1, 1, 1],
#        [1, 0, 1],
#        [1, 1, 1]])

这是一个空心环。内核将累加每个位置周围元素的数量。您需要对布尔值进行操作才能使其正常工作。

此外,由于您只想计算相似的元素,因此您需要创建一个数组,将独特的元素分离到各个平面中。碰巧的是,您可以同时创建一个布尔数组:

u = np.unique(a)  # array([0, 1, 2, 3])
b = a == u[1:, None, None]

如果您向内核添加一维,您现在可以直接应用卷积:

counts = convolve(b, kernel[None, ...], 'same')

这将在原始非零以外的位置返回非零,因此您必须对其进行屏蔽:

counts *= b

counts最后,您现在可以对额外标签维度的非零元素求和:

counts = counts.sum(0)

该卷积返回对角线元素以及直接邻居。要返回后者,请改用此内核:

kernel = np.zeros((3, 3), dtype=int)
kernel[0, 1] = kernel[2, 1] = kernel[1, 0] = kernel[1, 2] = 1
# array([[0, 1, 0],
#        [1, 0, 1],
#        [0, 1, 0]])

scipy 中的另一个卷积函数 , 在scipy.ndimage.convolve这里不太适合,尽管你也可以使用它:

counts = convolve(b.view(np.uint8), kernel, mode='constant', cval=0)

注意论点b.view(np.uint8)。您需要这样做或等效(例如b.astype(int))以避免获取布尔数组而不是计数。

TL;博士

from scipy.signal import convolve

kernel = np.ones((1, 3, 3), dtype=int)
kernel[0, 1, 1] = 0
b = a == np.unique(a)[1:].reshape(-1, 1, 1)
counts = (b * convolve(b, kernel, 'same')).sum(0)

推荐阅读