在矩阵中得到零的非对角镜像元素的最快方法是什么



假设有一个矩阵如下:

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_indicestril_indices。好的是,它们只是索引,所以如果所有矩阵的大小都相同,你可以缓存它们一次,而不复制任何特定的数据。另一个好处是,对于给定大小的ntriu_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.]])

最新更新