如何将函数应用于矩阵的每个元素?
How to apply a function to each element of a matrix?
对于矩阵的每个元素,我想计算 k 个最近的单元格的总和。
来自这个函数
def sum_neighbors(a, radius, row_number, column_number):
A = [[a[i][j] if i >= 0 and i < len(a) and j >= 0 and j < len(a[0]) else 0
for j in range(column_number-1-radius, column_number+radius)]
for i in range(row_number-1-radius, row_number+radius)]
return np.sum(A)
我可以做到。例如,如果我有以下矩阵
A
array([[1, 8, 2, 2, 8, 2, 4, 2, 6],
[3, 6, 9, 9, 1, 8, 0, 3, 9],
[7, 4, 2, 7, 8, 5, 8, 8, 1],
[8, 3, 2, 3, 6, 9, 9, 1, 6],
[1, 1, 3, 5, 6, 2, 9, 1, 8],
[7, 0, 8, 7, 9, 8, 4, 4, 6],
[1, 8, 5, 4, 7, 4, 8, 7, 0],
[2, 8, 0, 3, 5, 6, 4, 3, 9],
[1, 5, 1, 9, 9, 4, 5, 7, 9]])
我可以运行获取以下内容,对于每个值,半径为 2 个单元格的所有值的总和。
s = np.shape(A)
As = np.zeros(s)
for i in range(0,s[0]):
for j in range(0,s[1]):
As[i][j]=neighbors(A, 2, i, j)
As
array([[ 18., 29., 40., 49., 55., 45., 39., 43., 34.],
[ 29., 42., 60., 77., 81., 75., 75., 73., 56.],
[ 40., 55., 76., 99., 104., 104., 103., 104., 81.],
[ 42., 60., 86., 115., 121., 129., 126., 130., 101.],
[ 40., 64., 95., 125., 131., 147., 140., 139., 109.],
[ 40., 60., 86., 122., 126., 148., 149., 144., 108.],
[ 39., 57., 79., 112., 122., 136., 134., 141., 108.],
[ 34., 51., 79., 115., 127., 135., 140., 144., 108.],
[ 32., 46., 69., 99., 110., 110., 117., 118., 88.]])
但是,对于大型矩阵,循环需要无限的时间。有没有办法在没有循环的情况下将函数应用于矩阵的每个元素?
问题
处理边界问题的方法是用 k
零填充数组。由于您要将这些值加到数组中,因此没有任何效果——它只会让生活更轻松。考虑原始数组的 top-left 角:
1 8 2 ...
3 6 9 ...
7 4 2 ...
. .
. .
. .
例如,您想从 1
的每个方向观察距离 k = 2
的角落。通过用 k = 2
零填充 A
,我们得到
0 0 0 0 0 ...
0 0 0 0 0 ...
0 0 1 8 2 ...
0 0 3 6 9 ...
0 0 7 4 2 ...
. .
. .
. .
填充有助于边界,但此过程的技巧是利用 numpy 的 stride_tricks
sub-module。我们实际上是在执行 2D 卷积,是的,您可以为此使用 scipy
,如果您碰巧安装了它并且它已经依赖于您的项目。
解决方案
from numpy.lib.stride_tricks import sliding_window_view as windows
k = 2 # radius; k >= 1
w = 2*k + 1 # window size; 1x1, 3x3, 5x5, ...
# A = np.array(...) # from question
neighbor_sums = windows(np.pad(A, k), (w, w)).sum(axis=(2, 3))
k = 1
的输出:
array([[18, 29, 36, 31, 30, 23, 19, 24, 20],
[29, 42, 49, 48, 50, 44, 40, 41, 29],
[31, 44, 45, 47, 56, 54, 51, 45, 28],
[24, 31, 30, 42, 51, 62, 52, 51, 25],
[20, 33, 32, 49, 55, 62, 47, 48, 26],
[18, 34, 41, 54, 52, 57, 47, 47, 26],
[26, 39, 43, 48, 53, 55, 48, 45, 29],
[25, 31, 43, 43, 51, 52, 48, 52, 35],
[16, 17, 26, 27, 36, 33, 29, 37, 28]])
对于k = 2
:
array([[ 42, 60, 77, 81, 75, 75, 73, 56, 41],
[ 55, 76, 99, 104, 104, 103, 104, 81, 57],
[ 60, 86, 115, 121, 129, 126, 130, 101, 75],
[ 64, 95, 125, 131, 147, 140, 139, 109, 77],
[ 60, 86, 122, 126, 148, 149, 144, 108, 80],
[ 57, 79, 112, 122, 136, 134, 141, 108, 79],
[ 51, 79, 115, 127, 135, 140, 144, 108, 84],
[ 46, 69, 99, 110, 110, 117, 118, 88, 66],
[ 31, 47, 68, 78, 74, 85, 87, 66, 52]])
和scipy
如果您已经在项目中使用 scipy 作为依赖项,您不妨利用其专门的 convolve2d()
功能:
k = 2 # radius; k >= 1
w = 2*k + 1 # window size; 1x1, 3x3, 5x5, ...
neighbor_sums = scipy.signal.convolve2d(A, np.ones((w, w), int), mode='same')
性能
快速基准测试显示 scipy 的 convolve2D()
函数确实更快:
In [18]: ones = np.ones((w, w), int)
In [19]: padded_A = np.pad(A, k)
In [20]: %timeit convolve2d(A, ones, mode='same')
9.79 µs ± 786 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [21]: %timeit windows(padded_A, (w, w)).sum(axis=(2, 3))
34.5 µs ± 6.47 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
对于矩阵的每个元素,我想计算 k 个最近的单元格的总和。 来自这个函数
def sum_neighbors(a, radius, row_number, column_number):
A = [[a[i][j] if i >= 0 and i < len(a) and j >= 0 and j < len(a[0]) else 0
for j in range(column_number-1-radius, column_number+radius)]
for i in range(row_number-1-radius, row_number+radius)]
return np.sum(A)
我可以做到。例如,如果我有以下矩阵
A
array([[1, 8, 2, 2, 8, 2, 4, 2, 6],
[3, 6, 9, 9, 1, 8, 0, 3, 9],
[7, 4, 2, 7, 8, 5, 8, 8, 1],
[8, 3, 2, 3, 6, 9, 9, 1, 6],
[1, 1, 3, 5, 6, 2, 9, 1, 8],
[7, 0, 8, 7, 9, 8, 4, 4, 6],
[1, 8, 5, 4, 7, 4, 8, 7, 0],
[2, 8, 0, 3, 5, 6, 4, 3, 9],
[1, 5, 1, 9, 9, 4, 5, 7, 9]])
我可以运行获取以下内容,对于每个值,半径为 2 个单元格的所有值的总和。
s = np.shape(A)
As = np.zeros(s)
for i in range(0,s[0]):
for j in range(0,s[1]):
As[i][j]=neighbors(A, 2, i, j)
As
array([[ 18., 29., 40., 49., 55., 45., 39., 43., 34.],
[ 29., 42., 60., 77., 81., 75., 75., 73., 56.],
[ 40., 55., 76., 99., 104., 104., 103., 104., 81.],
[ 42., 60., 86., 115., 121., 129., 126., 130., 101.],
[ 40., 64., 95., 125., 131., 147., 140., 139., 109.],
[ 40., 60., 86., 122., 126., 148., 149., 144., 108.],
[ 39., 57., 79., 112., 122., 136., 134., 141., 108.],
[ 34., 51., 79., 115., 127., 135., 140., 144., 108.],
[ 32., 46., 69., 99., 110., 110., 117., 118., 88.]])
但是,对于大型矩阵,循环需要无限的时间。有没有办法在没有循环的情况下将函数应用于矩阵的每个元素?
问题
处理边界问题的方法是用 k
零填充数组。由于您要将这些值加到数组中,因此没有任何效果——它只会让生活更轻松。考虑原始数组的 top-left 角:
1 8 2 ...
3 6 9 ...
7 4 2 ...
. .
. .
. .
例如,您想从 1
的每个方向观察距离 k = 2
的角落。通过用 k = 2
零填充 A
,我们得到
0 0 0 0 0 ...
0 0 0 0 0 ...
0 0 1 8 2 ...
0 0 3 6 9 ...
0 0 7 4 2 ...
. .
. .
. .
填充有助于边界,但此过程的技巧是利用 numpy 的 stride_tricks
sub-module。我们实际上是在执行 2D 卷积,是的,您可以为此使用 scipy
,如果您碰巧安装了它并且它已经依赖于您的项目。
解决方案
from numpy.lib.stride_tricks import sliding_window_view as windows
k = 2 # radius; k >= 1
w = 2*k + 1 # window size; 1x1, 3x3, 5x5, ...
# A = np.array(...) # from question
neighbor_sums = windows(np.pad(A, k), (w, w)).sum(axis=(2, 3))
k = 1
的输出:
array([[18, 29, 36, 31, 30, 23, 19, 24, 20],
[29, 42, 49, 48, 50, 44, 40, 41, 29],
[31, 44, 45, 47, 56, 54, 51, 45, 28],
[24, 31, 30, 42, 51, 62, 52, 51, 25],
[20, 33, 32, 49, 55, 62, 47, 48, 26],
[18, 34, 41, 54, 52, 57, 47, 47, 26],
[26, 39, 43, 48, 53, 55, 48, 45, 29],
[25, 31, 43, 43, 51, 52, 48, 52, 35],
[16, 17, 26, 27, 36, 33, 29, 37, 28]])
对于k = 2
:
array([[ 42, 60, 77, 81, 75, 75, 73, 56, 41],
[ 55, 76, 99, 104, 104, 103, 104, 81, 57],
[ 60, 86, 115, 121, 129, 126, 130, 101, 75],
[ 64, 95, 125, 131, 147, 140, 139, 109, 77],
[ 60, 86, 122, 126, 148, 149, 144, 108, 80],
[ 57, 79, 112, 122, 136, 134, 141, 108, 79],
[ 51, 79, 115, 127, 135, 140, 144, 108, 84],
[ 46, 69, 99, 110, 110, 117, 118, 88, 66],
[ 31, 47, 68, 78, 74, 85, 87, 66, 52]])
和scipy
如果您已经在项目中使用 scipy 作为依赖项,您不妨利用其专门的 convolve2d()
功能:
k = 2 # radius; k >= 1
w = 2*k + 1 # window size; 1x1, 3x3, 5x5, ...
neighbor_sums = scipy.signal.convolve2d(A, np.ones((w, w), int), mode='same')
性能
快速基准测试显示 scipy 的 convolve2D()
函数确实更快:
In [18]: ones = np.ones((w, w), int)
In [19]: padded_A = np.pad(A, k)
In [20]: %timeit convolve2d(A, ones, mode='same')
9.79 µs ± 786 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [21]: %timeit windows(padded_A, (w, w)).sum(axis=(2, 3))
34.5 µs ± 6.47 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)