假设有一个矩阵如下:
a = np.array([[74, 0, 2],
[ 0, 73, 8],
[ 0, 10, 72]])
我想在上三角形和下三角形中找到零的镜像元素,并将它们设置为nan
。例如,在这种情况下,a[0, 1]
和a[1, 0]
。我可以写一个循环像:
m = np.zeros((3, 3))
for i in range(a.shape[0]):
for j in range(a.shape[1]):
if i == j:
m[i, j] = a[i, j]
continue
if (a[i, j] == 0) & (a[j, i] == 0):
m[i, j] = np.nan
m[j, i] = np.nan
continue
m[i, j] = a[i, j]
m[j, i] = a[j, i]
print(m)
[[74. nan 2.]
[nan 73. 8.]
[ 0. 10. 72.]]
这就完成了任务。但我有数百万这样的矩阵,我想知道什么是更好、更快的方法。
根据我的评论建议,这里有另一种选择。注意;ndiag";若对角线上永远不会有零,那个么这个东西就不需要了。
import numpy as np
ndiag = 1-np.eye(3)
print(ndiag)
a = np.array( [[74,0,2],[0,73,8],[0,10,72]] ).astype(float)
m = a == 0
print( m )
m = np.logical_and( ndiag, np.logical_and( m, m.T ) )
print( m )
a[m] = np.nan
print( a )
输出:
[[0 1 1]
[1 0 1]
[1 1 0]]
[[False True False]
[ True False False]
[ True False False]]
[[False True False]
[ True False False]
[False False False]]
[[74. nan 2.]
[nan 73. 8.]
[ 0. 10. 72.]]
对于这类任务,我一直偏爱triu_indices
和tril_indices
。好的是,它们只是索引,所以如果所有矩阵的大小都相同,你可以缓存它们一次,而不复制任何特定的数据。另一个好处是,对于给定大小的n
,triu_indices(n, 1)
是tril_indices(n, -1)
的交换结果,可以进行一些通常不关心的排序。
所以如果你所有的矩阵都是(n, n)
的形状,
rows, cols = np.triu_indices(n, 1)
mask = (a[rows, cols] == a[cols, rows]) & (a[rows, cols] != 0)
a[rows[mask], cols[mask]] = a[cols[mask], rows[mask]] = np.nan
请记住,不能将np.nan
分配给数组,除非它是浮点类型。此外,您可能会从预计算rows[mask]
和cols[mask]
:中获得一点点里程
rm = rows[mask]
cm = cols[mask]
a[rm, cm] = a[cm, rm] = np.nan
这里有一种完全矢量化的方法来解决这个问题-
np.where(np.logical_and(np.tril(a) == np.triu(a).T, a==0), np.nan, a)
array([[74., nan, 2.],
[nan, 73., 8.],
[ 0., 10., 72.]])
说明-
让我们看看第一步会发生什么-
np.tril(a) #keeps only the lower triangular, and others become 0
array([[74, 0, 0],
[ 0, 73, 0],
[ 0, 10, 72]])
np.triu(a).T #keeps only the upper triangular and others become 0. Then flips it to become lower triangular
array([[74, 0, 0],
[ 0, 73, 0],
[ 2, 8, 72]])
等式将使上三角部分始终为True,而下三角矩阵仅包含镜像匹配元素的True。
np.tril(a) == np.triu(a).T
array([[ True, True, True],
[ True, True, True],
[False, False, True]])
现在,当你用a==0矩阵取这个布尔值的logical_and时,只剩下原始矩阵为0并且是镜像元素的值。
np.logical_and(np.tril(a) == np.triu(a).T, a==0)
array([[False, True, False],
[ True, False, False],
[False, False, False]])
现在可以使用np.where
将True值替换为nan,并保持其余值不变。
np.where(np.logical_and(np.tril(a) == np.triu(a).T, a==0), np.nan, a)
array([[74., nan, 2.],
[nan, 73., 8.],
[ 0., 10., 72.]])