沿二维数组中的对角线和反对角线求和 - NumPy / Python
Sum along diagonal and anti-diagonal lines in 2D array - NumPy / Python
我有一个 np 数组,如下所示:
np.array([[1,0,0],[1,0,0],[0,1,0]])
output:
array([[1, 0, 0],
[1, 0, 0],
[0, 1, 0]])
我想将左对角线和右对角线元素相加到新数组中:
1) 左对角线:
输出:
[1,1,0,1,0]
2) 右对角线:
输出:
[0,0,1,2,0]
有没有简单的方法?谢谢~
您可以像这样使用 .diag
:
import numpy as np
arr = np.array([[1, 0, 0], [1, 0, 0], [0, 1, 0]])
left_diag = [np.sum(np.diag(np.fliplr(arr), d)) for d in range(len(arr) - 1, -len(arr), -1)]
right_diag = [np.sum(np.diag(arr, d)) for d in range(len(arr) - 1, -len(arr), -1)]
print("left:", left_diag)
print("right:", right_diag)
输出:
left: [1, 1, 0, 1, 0]
right: [0, 0, 1, 2, 0]
它returns给定偏移对角线上的所有元素。为了按照您提到的顺序获得所有偏移量,我们从 +2 到 -2,然后对于每个对角线获得元素的总和。
为了获得左对角线,我们首先使用 .fliplr
翻转 arr
方法 #1:使用掩码
这是基于 masking
-
的矢量化
def left_diag_sum_masking(a):
n = len(a)
N = 2*n-1
R = np.arange(N)
r = np.arange(n)
mask = (r[:,None] <= R) & (r[:,None]+n > R)
b = np.zeros(mask.shape,dtype=a.dtype)
b[mask] = a.ravel()
return b.sum(0)
所以,left_diag_sum_masking
给了我们 left-diagonal 总和。要获得 right-diagonal 个,只需沿着 cols 翻转,求和然后翻转回来。
因此,只需执行 -
right_diag_sum = left_diag_sum_masking(a[::-1])[::-1]
样本运行-
In [220]: np.random.seed(0)
In [221]: a = np.random.randint(0,9,(4,4))
In [222]: a
Out[222]:
array([[5, 0, 3, 3],
[7, 3, 5, 2],
[4, 7, 6, 8],
[8, 1, 6, 7]])
In [223]: left_diag_sum_masking(a)
Out[223]: array([ 5, 7, 10, 23, 9, 14, 7])
In [224]: left_diag_sum_masking(a[::-1])[::-1] # right-diag sums
Out[224]: array([ 3, 5, 13, 21, 20, 5, 8])
方法 #2:使用 zeros-padding
def left_diag_sum_zerospad(a):
n = len(a)
N = 2*n-1
p = np.zeros((n,n),dtype=a.dtype)
ap = np.concatenate((a,p),axis=1)
return ap.ravel()[:n*N].reshape(n,-1).sum(0)
得到right-diagonal个求和-
right_diag_sum = left_diag_sum_zerospad(a[::-1])[::-1]
numpy 作为此跟踪的一个非常有用的函数,这里是一个示例
'''
将 numpy 导入为 np
a=np.array([[1,0,0],[1,0,0],[0,1,0]])
b=np.trace(a)
打印(b)
'''
Here is the link to documentation
另一种方法,基于填充和滚动:
def sum_shifted(arr, direction=1):
n = arr.shape[0]
temp = np.zeros((n, 2 * n - 1), dtype=arr.dtype)
temp[:, slice(None, n) if direction == 1 else slice(-n, None)] = arr
for i in range(n):
temp[i, :] = np.roll(temp[i, :], direction * i)
return np.sum(temp, 0)[::direction]
这为您提供了很多选择。
速度方面,@Divakar 的方法似乎有优势:
这些图是使用 this script 生成的,使用这些作为测试函数:
def sum_shifted(arr, direction=1):
n = arr.shape[0]
temp = np.zeros((n, 2 * n - 1), dtype=arr.dtype)
temp[:, slice(None, n) if direction == 1 else slice(-n, None)] = arr
for i in range(n):
temp[i, :] = np.roll(temp[i, :], direction * i)
return np.sum(temp, 0)[::direction]
def sum_shifted_both(arr):
return sum_shifted(arr, 1), sum_shifted(arr, -1)
def sum_adam(arr):
return (
np.array([np.sum(np.diag(np.fliplr(arr), d)) for d in range(len(arr) - 1, -len(arr), -1)]),
np.array([np.sum(np.diag(arr, d)) for d in range(len(arr) - 1, -len(arr), -1)]))
def sum_divakar(a):
n = len(a)
N = 2*n-1
R = np.arange(N)
r = np.arange(n)
mask = (r[:,None] <= R) & (r[:,None]+n > R)
b_leftdiag = np.zeros(mask.shape,dtype=a.dtype)
b_leftdiag[mask] = a.ravel()
b_rightdiag = np.zeros(mask.shape,dtype=a.dtype)
b_rightdiag[mask[:,::-1]] = a.ravel()
return b_leftdiag.sum(0), b_rightdiag.sum(0)[::-1]
def sum_divakar2(a):
def left_sum(a):
n = len(a)
N = 2*n-1
p = np.zeros((n,n),dtype=a.dtype)
ap = np.concatenate((a,p),axis=1)
return ap.ravel()[:n*N].reshape(n,-1).sum(0)
return left_sum(a), left_sum(a[::-1])[::-1]
作为辅助函数:
def gen_input(n):
return np.arange(n * n).reshape((n, n))
def equal_output(out_a, out_b):
return all(
np.all(a_arr == b_arr)
for a_arr, b_arr in zip(out_a, out_b))
input_sizes=(5, 10, 50, 100, 500, 1000, 5000)
funcs = sum_shifted_both, sum_adam, sum_divakar, sum_divakar2
runtimes, input_sizes, labels, results = benchmark(
funcs, gen_input=gen_input, equal_output=equal_output, input_sizes=input_sizes)
plot_benchmarks(runtimes, input_sizes, labels)
即使对于非方阵也适用的快速解决方案:
from numpy.lib.stride_tricks import as_strided
def left_diag_sum(matrix):
n_rows, n_cols = matrix.shape
if n_rows > n_cols:
matrix = matrix.T
n_rows, n_cols = n_cols, n_rows
diag_len = n_rows
n_diags = n_rows + n_cols - 1
dtype = matrix.dtype
leaning = np.zeros((diag_len, n_diags), dtype)
col_dim_stride = dtype.itemsize
row_dim_stride = col_dim_stride * (n_diags + 1)
strided = as_strided(leaning, matrix.shape, (row_dim_stride, col_dim_stride))
strided[...] = matrix
return leaning.sum(0)
def right_diag_sum(matrix):
row_reversed_matrix = matrix[::-1]
return left_diag_sum(row_reversed_matrix)[::-1]
我有一个 np 数组,如下所示:
np.array([[1,0,0],[1,0,0],[0,1,0]])
output:
array([[1, 0, 0],
[1, 0, 0],
[0, 1, 0]])
我想将左对角线和右对角线元素相加到新数组中:
1) 左对角线:
输出:
[1,1,0,1,0]
2) 右对角线:
输出:
[0,0,1,2,0]
有没有简单的方法?谢谢~
您可以像这样使用 .diag
:
import numpy as np
arr = np.array([[1, 0, 0], [1, 0, 0], [0, 1, 0]])
left_diag = [np.sum(np.diag(np.fliplr(arr), d)) for d in range(len(arr) - 1, -len(arr), -1)]
right_diag = [np.sum(np.diag(arr, d)) for d in range(len(arr) - 1, -len(arr), -1)]
print("left:", left_diag)
print("right:", right_diag)
输出:
left: [1, 1, 0, 1, 0]
right: [0, 0, 1, 2, 0]
它returns给定偏移对角线上的所有元素。为了按照您提到的顺序获得所有偏移量,我们从 +2 到 -2,然后对于每个对角线获得元素的总和。
为了获得左对角线,我们首先使用 .fliplr
方法 #1:使用掩码
这是基于 masking
-
def left_diag_sum_masking(a):
n = len(a)
N = 2*n-1
R = np.arange(N)
r = np.arange(n)
mask = (r[:,None] <= R) & (r[:,None]+n > R)
b = np.zeros(mask.shape,dtype=a.dtype)
b[mask] = a.ravel()
return b.sum(0)
所以,left_diag_sum_masking
给了我们 left-diagonal 总和。要获得 right-diagonal 个,只需沿着 cols 翻转,求和然后翻转回来。
因此,只需执行 -
right_diag_sum = left_diag_sum_masking(a[::-1])[::-1]
样本运行-
In [220]: np.random.seed(0)
In [221]: a = np.random.randint(0,9,(4,4))
In [222]: a
Out[222]:
array([[5, 0, 3, 3],
[7, 3, 5, 2],
[4, 7, 6, 8],
[8, 1, 6, 7]])
In [223]: left_diag_sum_masking(a)
Out[223]: array([ 5, 7, 10, 23, 9, 14, 7])
In [224]: left_diag_sum_masking(a[::-1])[::-1] # right-diag sums
Out[224]: array([ 3, 5, 13, 21, 20, 5, 8])
方法 #2:使用 zeros-padding
def left_diag_sum_zerospad(a):
n = len(a)
N = 2*n-1
p = np.zeros((n,n),dtype=a.dtype)
ap = np.concatenate((a,p),axis=1)
return ap.ravel()[:n*N].reshape(n,-1).sum(0)
得到right-diagonal个求和-
right_diag_sum = left_diag_sum_zerospad(a[::-1])[::-1]
numpy 作为此跟踪的一个非常有用的函数,这里是一个示例
''' 将 numpy 导入为 np a=np.array([[1,0,0],[1,0,0],[0,1,0]]) b=np.trace(a) 打印(b) ''' Here is the link to documentation
另一种方法,基于填充和滚动:
def sum_shifted(arr, direction=1):
n = arr.shape[0]
temp = np.zeros((n, 2 * n - 1), dtype=arr.dtype)
temp[:, slice(None, n) if direction == 1 else slice(-n, None)] = arr
for i in range(n):
temp[i, :] = np.roll(temp[i, :], direction * i)
return np.sum(temp, 0)[::direction]
这为您提供了很多选择。 速度方面,@Divakar 的方法似乎有优势:
这些图是使用 this script 生成的,使用这些作为测试函数:
def sum_shifted(arr, direction=1):
n = arr.shape[0]
temp = np.zeros((n, 2 * n - 1), dtype=arr.dtype)
temp[:, slice(None, n) if direction == 1 else slice(-n, None)] = arr
for i in range(n):
temp[i, :] = np.roll(temp[i, :], direction * i)
return np.sum(temp, 0)[::direction]
def sum_shifted_both(arr):
return sum_shifted(arr, 1), sum_shifted(arr, -1)
def sum_adam(arr):
return (
np.array([np.sum(np.diag(np.fliplr(arr), d)) for d in range(len(arr) - 1, -len(arr), -1)]),
np.array([np.sum(np.diag(arr, d)) for d in range(len(arr) - 1, -len(arr), -1)]))
def sum_divakar(a):
n = len(a)
N = 2*n-1
R = np.arange(N)
r = np.arange(n)
mask = (r[:,None] <= R) & (r[:,None]+n > R)
b_leftdiag = np.zeros(mask.shape,dtype=a.dtype)
b_leftdiag[mask] = a.ravel()
b_rightdiag = np.zeros(mask.shape,dtype=a.dtype)
b_rightdiag[mask[:,::-1]] = a.ravel()
return b_leftdiag.sum(0), b_rightdiag.sum(0)[::-1]
def sum_divakar2(a):
def left_sum(a):
n = len(a)
N = 2*n-1
p = np.zeros((n,n),dtype=a.dtype)
ap = np.concatenate((a,p),axis=1)
return ap.ravel()[:n*N].reshape(n,-1).sum(0)
return left_sum(a), left_sum(a[::-1])[::-1]
作为辅助函数:
def gen_input(n):
return np.arange(n * n).reshape((n, n))
def equal_output(out_a, out_b):
return all(
np.all(a_arr == b_arr)
for a_arr, b_arr in zip(out_a, out_b))
input_sizes=(5, 10, 50, 100, 500, 1000, 5000)
funcs = sum_shifted_both, sum_adam, sum_divakar, sum_divakar2
runtimes, input_sizes, labels, results = benchmark(
funcs, gen_input=gen_input, equal_output=equal_output, input_sizes=input_sizes)
plot_benchmarks(runtimes, input_sizes, labels)
即使对于非方阵也适用的快速解决方案:
from numpy.lib.stride_tricks import as_strided
def left_diag_sum(matrix):
n_rows, n_cols = matrix.shape
if n_rows > n_cols:
matrix = matrix.T
n_rows, n_cols = n_cols, n_rows
diag_len = n_rows
n_diags = n_rows + n_cols - 1
dtype = matrix.dtype
leaning = np.zeros((diag_len, n_diags), dtype)
col_dim_stride = dtype.itemsize
row_dim_stride = col_dim_stride * (n_diags + 1)
strided = as_strided(leaning, matrix.shape, (row_dim_stride, col_dim_stride))
strided[...] = matrix
return leaning.sum(0)
def right_diag_sum(matrix):
row_reversed_matrix = matrix[::-1]
return left_diag_sum(row_reversed_matrix)[::-1]