二维索引和总和的numpy数组
numpy array of 2 dimensions indexing and sum
我有疑问。有一种有效的方法可以在不使用多个条件的情况下对 numpy 矩阵的所有邻居求和吗?
这是一个例子:
array([[5, 4, 8, 3, 1, 4, 3, 2, 2, 3],
[2, 7, 4, 5, 8, 5, 4, 7, 1, 1],
[5, 2, 6, 4, 5, 5, 6, 1, 7, 3],
[6, 1, 4, 1, 3, 3, 6, 1, 4, 6],
[6, 3, 5, 7, 3, 8, 5, 4, 7, 8],
[4, 1, 6, 7, 5, 2, 4, 6, 4, 5],
[2, 1, 7, 6, 8, 4, 1, 7, 2, 1],
[6, 8, 8, 2, 8, 8, 1, 1, 3, 4],
[4, 8, 4, 6, 8, 4, 8, 5, 5, 4],
[5, 2, 8, 3, 7, 5, 1, 5, 2, 6]])
当我 运行 m[0][-1]
它 returns 我 3 而不是错误,所以如果我想给一个值的所有邻居加 1 我需要使用很多条件,因为我不能只使用 m[0][-1]
因为在这种情况下和其他角落的情况下 returns 我只是一个“假邻居”
IIUC,您想要给具有给定值的单元格的每个相邻单元格加 1。
例如,让我们将 1 加到 7 附近的每个单元格:
from scipy.signal import convolve2d
v = np.array([[1,1,1],[1,0,1],[1,1,1]])
a + convolve2d(a==7, v, mode='same')
输出:
array([[6, 5, 9, 3, 1, 4, 4, 3, 3, 3],
[3, 7, 5, 5, 8, 5, 5, 8, 3, 2],
[6, 3, 7, 4, 5, 5, 7, 3, 8, 4],
[6, 1, 5, 2, 4, 3, 6, 3, 6, 8],
[6, 3, 7, 8, 5, 8, 5, 5, 7, 9],
[4, 2, 9, 9, 7, 2, 5, 8, 6, 6],
[2, 2, 8, 8, 9, 4, 2, 7, 3, 1],
[6, 9, 9, 3, 8, 8, 2, 2, 4, 4],
[4, 8, 4, 7, 9, 5, 8, 5, 5, 4],
[5, 2, 8, 4, 7, 6, 1, 5, 2, 6]])
除了好的@mozway 解决方案之外,一个非常有效的解决方案是使用 Numba 模板装饰器并结合并行执行。这是一个例子:
import numba as nb
# parallel=True is only useful for quite-big arrays
@nb.njit(parallel=True)
def kernel(v):
cond = np.zeros((v.shape[0]+2, v.shape[1]+2), dtype=np.bool_)
cond[1:-1, 1:-1] = v == 7
res = nb.stencil(lambda c: c[-1,-1]+c[-1,0]+c[-1,1]+c[0,-1]+c[0,1]+c[1,-1]+c[1,0]+c[1,1])(cond)
return v + res[1:-1, 1:-1]
kernel(m)
一个更快的解决方案是 就地 (使用 v += res
而不是 return v + res
)。以下是 2000x2000 整数数组在我的 6 核机器上的性能结果:
scipy.signal.convolve2d: 124 ms
Numba out-of-place: 20 ms
Numba in-place: 15 ms
请注意,由于编译时间的原因,对 kernel
的第一次调用较慢。
对于较小的阵列 (200x200),我也获得了类似的加速。
我有疑问。有一种有效的方法可以在不使用多个条件的情况下对 numpy 矩阵的所有邻居求和吗?
这是一个例子:
array([[5, 4, 8, 3, 1, 4, 3, 2, 2, 3],
[2, 7, 4, 5, 8, 5, 4, 7, 1, 1],
[5, 2, 6, 4, 5, 5, 6, 1, 7, 3],
[6, 1, 4, 1, 3, 3, 6, 1, 4, 6],
[6, 3, 5, 7, 3, 8, 5, 4, 7, 8],
[4, 1, 6, 7, 5, 2, 4, 6, 4, 5],
[2, 1, 7, 6, 8, 4, 1, 7, 2, 1],
[6, 8, 8, 2, 8, 8, 1, 1, 3, 4],
[4, 8, 4, 6, 8, 4, 8, 5, 5, 4],
[5, 2, 8, 3, 7, 5, 1, 5, 2, 6]])
当我 运行 m[0][-1]
它 returns 我 3 而不是错误,所以如果我想给一个值的所有邻居加 1 我需要使用很多条件,因为我不能只使用 m[0][-1]
因为在这种情况下和其他角落的情况下 returns 我只是一个“假邻居”
IIUC,您想要给具有给定值的单元格的每个相邻单元格加 1。
例如,让我们将 1 加到 7 附近的每个单元格:
from scipy.signal import convolve2d
v = np.array([[1,1,1],[1,0,1],[1,1,1]])
a + convolve2d(a==7, v, mode='same')
输出:
array([[6, 5, 9, 3, 1, 4, 4, 3, 3, 3],
[3, 7, 5, 5, 8, 5, 5, 8, 3, 2],
[6, 3, 7, 4, 5, 5, 7, 3, 8, 4],
[6, 1, 5, 2, 4, 3, 6, 3, 6, 8],
[6, 3, 7, 8, 5, 8, 5, 5, 7, 9],
[4, 2, 9, 9, 7, 2, 5, 8, 6, 6],
[2, 2, 8, 8, 9, 4, 2, 7, 3, 1],
[6, 9, 9, 3, 8, 8, 2, 2, 4, 4],
[4, 8, 4, 7, 9, 5, 8, 5, 5, 4],
[5, 2, 8, 4, 7, 6, 1, 5, 2, 6]])
除了好的@mozway 解决方案之外,一个非常有效的解决方案是使用 Numba 模板装饰器并结合并行执行。这是一个例子:
import numba as nb
# parallel=True is only useful for quite-big arrays
@nb.njit(parallel=True)
def kernel(v):
cond = np.zeros((v.shape[0]+2, v.shape[1]+2), dtype=np.bool_)
cond[1:-1, 1:-1] = v == 7
res = nb.stencil(lambda c: c[-1,-1]+c[-1,0]+c[-1,1]+c[0,-1]+c[0,1]+c[1,-1]+c[1,0]+c[1,1])(cond)
return v + res[1:-1, 1:-1]
kernel(m)
一个更快的解决方案是 就地 (使用 v += res
而不是 return v + res
)。以下是 2000x2000 整数数组在我的 6 核机器上的性能结果:
scipy.signal.convolve2d: 124 ms
Numba out-of-place: 20 ms
Numba in-place: 15 ms
请注意,由于编译时间的原因,对 kernel
的第一次调用较慢。
对于较小的阵列 (200x200),我也获得了类似的加速。