我们说我有一个numpy数组
a b c
A = i j k
u v w
我想比较值中心元素与其八个相邻元素中的一些(沿轴或沿对角线)。有没有更快的方法,除了嵌套的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
...
像这样的东西。
答案 0 :(得分:1)
您的操作包含许多条件,因此在一般情况下(任何类型的条件,任何类型的操作)执行此操作的最有效方法是使用循环。这可以使用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]
])
我们会在A
中找到适用各种条件的地点。
在A
中查找指定值出现的位置:
cond1a = A == 1
cond2a = A == 2
这给出了布尔值的矩阵,其大小与A
相同。条件成立时该值为true,否则为false。
在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)
和以前一样,这给出了表示条件为真的布尔值的矩阵。此代码使用scipy.ndimage.maximum_filter()。该功能迭代地移动一个“足迹”。以A
的每个元素为中心。该位置的返回值是足迹为1的所有元素的最大值。mode
参数指定如何处理矩阵边界外的隐式值,其中足迹落在边缘之外。在这里,我们将它们视为负无穷大,这与忽略它们相同(因为我们使用了最大操作)。
根据条件设置结果的值。如果条件1a和1b都为真,或者条件2a和2b都为真,则值为999。否则,该值为0.
result = np.zeros(A.shape)
result[(cond1a & cond1b) | (cond2a & cond2b)] = 999
结果是:
[
[ 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]
]
您可以通过更改过滤器占用空间来将此方法推广到其他邻居模式。您可以使用其他类型的过滤器推广到其他操作(最小值,中位数,百分位数等)(请参阅scipy.ndimage)。对于可以表示为加权和的操作,请使用2d cross correlation。
这种方法应该比在python中循环快得多。但是,它确实执行了不必要的计算(例如,当值为1或2时,它只需要计算最大值,但我们会为所有元素执行此操作)。手动循环可以让您避免这些计算。在python中循环可能比这里的代码慢得多。但是,在numba或cython中实现它可能会更快,因为这些工具会生成编译代码。
答案 1 :(得分:0)
我使用了numpy的:
concatenate
用零填充dstack
和roll
正确对齐沿不同尺寸应用custom_roll
两次并减去原件。
import numpy as np
def custom_roll(a, axis=0):
n = 3
a = a.T if axis==1 else a
pad = np.zeros((n-1, a.shape[1]))
a = np.concatenate([a, pad], axis=0)
ad = np.dstack([np.roll(a, i, axis=0) for i in range(n)])
a = ad.sum(2)[1:-1, :]
a = a.T if axis==1 else a
return a
考虑以下ndarray
:
A = np.arange(25).reshape(5, 5)
A
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24]])
sum_of_eight_around_me = custom_roll(custom_roll(A), axis=1) - A
sum_of_eight_around_me
array([[ 12., 20., 25., 30., 20.],
[ 28., 48., 56., 64., 42.],
[ 53., 88., 96., 104., 67.],
[ 78., 128., 136., 144., 92.],
[ 52., 90., 95., 100., 60.]])