给定输入
A = np.array([[1,2,3],[4,5,6],[7,8,9]])
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
需要输出:
array([[2, 3],
[4, 6],
[7, 8]])
使用迭代或循环来做到这一点很容易,但应该有一种简洁的方法可以在不使用循环的情况下做到这一点。谢谢
方法 #1
一种方法masking
-
A[~np.eye(A.shape[0],dtype=bool)].reshape(A.shape[0],-1)
示例运行 -
In [395]: A
Out[395]:
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
In [396]: A[~np.eye(A.shape[0],dtype=bool)].reshape(A.shape[0],-1)
Out[396]:
array([[2, 3],
[4, 6],
[7, 8]])
方法#2
使用非对角线元素的常规模式,这些元素可以通过范围数组的广播加法进行跟踪 -
m = A.shape[0]
idx = (np.arange(1,m+1) + (m+1)*np.arange(m-1)[:,None]).reshape(m,-1)
out = A.ravel()[idx]
方法#3(大步出击!
滥用以前方法中非对角线元素的常规模式,我们可以引入np.lib.stride_tricks.as_strided
和一些slicing
帮助,例如 -
m = A.shape[0]
strided = np.lib.stride_tricks.as_strided
s0,s1 = A.strides
out = strided(A.ravel()[1:], shape=(m-1,m), strides=(s0+s1,s1)).reshape(m,-1)
运行时测试
作为函数的方法:
def skip_diag_masking(A):
return A[~np.eye(A.shape[0],dtype=bool)].reshape(A.shape[0],-1)
def skip_diag_broadcasting(A):
m = A.shape[0]
idx = (np.arange(1,m+1) + (m+1)*np.arange(m-1)[:,None]).reshape(m,-1)
return A.ravel()[idx]
def skip_diag_strided(A):
m = A.shape[0]
strided = np.lib.stride_tricks.as_strided
s0,s1 = A.strides
return strided(A.ravel()[1:], shape=(m-1,m), strides=(s0+s1,s1)).reshape(m,-1)
计时-
In [528]: A = np.random.randint(11,99,(5000,5000))
In [529]: %timeit skip_diag_masking(A)
...: %timeit skip_diag_broadcasting(A)
...: %timeit skip_diag_strided(A)
...:
10 loops, best of 3: 56.1 ms per loop
10 loops, best of 3: 82.1 ms per loop
10 loops, best of 3: 32.6 ms per loop
解决方案步骤:
- 展平阵列
- 删除位于位置
range(0, len(x_no_diag), len(x) + 1)
的对角线元素的位置 - 将阵列调整为 (num_rows、num_columns - 1)
该函数:
import numpy as np
def remove_diag(x):
x_no_diag = np.ndarray.flatten(x)
x_no_diag = np.delete(x_no_diag, range(0, len(x_no_diag), len(x) + 1), 0)
x_no_diag = x_no_diag.reshape(len(x), len(x) - 1)
return x_no_diag
例:
>>> x = np.random.randint(5, size=(3,3))
array([[0, 2, 3],
[3, 4, 1],
[2, 4, 0]])
>>> remove_diag(x)
array([[2, 3],
[3, 1],
[2, 4]])
我参加这个聚会迟到了,但我有一个我认为是更简单的解决方案。所以你想删除对角线?好的酷:
- 将其替换为 NaN
- 过滤除 NaN 以外的所有内容(这会转换为一维,因为它不能假设结果是平方的)
- 重置维度
'
arr = np.array([[1,2,3],[4,5,6],[7,8,9]]).astype(np.float)
np.fill_diagonal(arr, np.nan)
arr[~np.isnan(arr)].reshape(arr.shape[0], arr.shape[1] - 1)
仅使用 numpy,假设一个方阵:
new_A = numpy.delete(A,range(0,A.shape[0]**2,(A.shape[0]+1))).reshape(A.shape[0],(A.shape[1]-1))
也许最干净的方法,基于 Divakar 的第一个解决方案,但使用 len(array)
而不是 array.shape[0]
,是:
array_without_diagonal = array[~np.eye(len(array), dtype=bool)].reshape(len(array), -1)
如果你不介意创建一个新数组,那么你可以使用列表推导。
A = np.array([A[i][A[i] != A[i][i]] for i in range(len(A))])
重新运行与@Divakar相同的方法,
A = np.random.randint(11,99,(5000,5000))
skip_diag_masking
85.7 ms ± 1.55 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
skip_diag_broadcasting
163 ms ± 1.77 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
skip_diag_strided
52.5 ms ± 2.54 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
skip_diag_list_comp
101 ms ± 347 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
这里的所有答案,但想添加一个,以防您的 numpy 对象超过 2 维。在这种情况下,您可以使用 Divakar 方法 #1 的以下调整:
def remove_diag(A):
removed = A[~np.eye(A.shape[0], dtype=bool)].reshape(A.shape[0], int(A.shape[0])-1, -1)
return np.squeeze(removed)
另一种方法是使用 numpy.delete()。 假设方阵,您可以使用:
numpy.delete(A,range(0,A.shape[0]**2,A.shape[0])).reshape(A.shape[0],A.shape[1]-1)