找出两个对称矩阵在行/列的排列中是否相同



我有两个对称(项目共现(矩阵A和B,想知道它们是否描述了相同的共现,只是行/列标签排列好了。(相同的排列必须应用于行和列,以保持对称/共现特性(

例如,在我的测试中,这两个矩阵应该相等:

a = np.array([
#1 #2 #3 #4 #5 #6 #7
[0, 1, 1, 0, 0, 0, 1], #1
[1, 0, 1, 2, 1, 1, 2], #2
[1, 1, 0, 0, 0, 0, 1], #3
[0, 2, 0, 0, 4, 0, 4], #4
[0, 1, 0, 4, 0, 1, 2], #5
[0, 1, 0, 0, 1, 0, 0], #6
[1, 2, 1, 4, 2, 0, 0]  #7
])
b = np.array([
#5 #7 #1,3#3,1#2 #4 #6
[0, 2, 0, 0, 1, 4, 1], #5
[2, 0, 1, 1, 2, 4, 0], #7
[0, 1, 0, 1, 1, 0, 0], #1,3 could be either
[0, 1, 1, 0, 1, 0, 0], #1,3 could be either
[1, 2, 1, 1, 0, 2, 1], #2
[4, 4, 0, 0, 2, 0, 0], #4
[1, 0, 0, 0, 1, 0, 0]  #6
])

我目前使用numpy.linalg.eigvals测试特征值是否相同(我甚至不确定这是否是一个充分条件(,但我想找到一个不涉及数值精度的测试,因为我在这里处理的是整数。

以下是一个基于sorting并利用searchsorted-的矢量化解决方案

import pandas as pd
# Sort rows for a and b
aS = np.sort(a,axis=1)
bS = np.sort(b,axis=1)
# Scale down each row to a scalar each
scale = np.r_[(np.maximum(aS.max(0),bS.max(0))+1)[::-1].cumprod()[::-1][1:],1]
aS1D = aS.dot(scale)
bS1D = bS.dot(scale)
# Use searchsorted to get the correspondence on indexing
sidx = aS1D.argsort()
searchsorted_idx = np.searchsorted(aS1D,bS1D,sorter=sidx)
searchsorted_idx[searchsorted_idx==len(aS1D)] = len(aS1D)-1
df = pd.DataFrame({'A':searchsorted_idx})
new_order = sidx[df.groupby('A').cumcount().values+searchsorted_idx]
# new_order is the permuted order, i.e. [5, 7, 1, 3, 2, 4, 6]
# Finally index into a with the new_order and compare against b
out = np.array_equal(a[new_order[:,None], new_order],b)

我假设您有a的行/列排列列表,它给出了b,例如类似于以下

p = np.array([5, 7, 1, 3, 2, 4, 6]) - 1

然后您可以在a上简单地执行以下操作

a_p = a[p]
a_p = a_p[:, p]

并检查CCD_ 7和排列的CCD_

(a_p == b).all()

编辑:因为您没有像上面的p那样的列表,所以您可以(至少对于小数组ab(生成索引的排列并检查每一个:

from itertools import permutations
def a_p(a, b, p):
p = np.array(p)
a_p = a[p]
a_p = a_p[:, p]
return a_p
for p in permutations(range(a.shape[0])):
if (a_p(a, b, p) == b).all():
print('True')
break
else:
print('False')

请注意,这种强力方法也适用于非对称矩阵。但是,由于大阵列ab的排列数量很大,因此这种方法可能非常缓慢。因此,计算特征值的解决方案要好得多。

这里有一个基准:

def Yduqoli(a, b):
''' I suppose your solution is similar'''
if (np.array(np.unique(a, return_counts=True)) == np.array(np.unique(b, return_counts=True))).all():
a_eigs = np.sort(np.linalg.eigvals(a))
b_eigs = np.sort(np.linalg.eigvals(b))
return np.allclose(a_eigs, b_eigs)
else:
return False
def AndyK(a, b):
for p in permutations(range(a.shape[0])):
if (a_p(a, b, p) == b).all():
return True
return False  
%timeit AndyK(a,b)
103 ms ± 4.54 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit Yduqoli(a,b)
408 µs ± 65.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

其中我使用了由OP提供的对称矩阵CCD_ 14和CCD_

更新:正如Paul Panzer所提到的,在某些情况下,简单地检查本征值可能会给出错误的结果,例如a = np.array([[4, 0], [0, 0]])b = np.array([[2, 2], [2, 2]])具有相同的本征值,但不能将一个本征值混洗到另一个。因此,我们首先需要检查阵列ab是否具有相同的元素(无论它们的位置如何(。

您可以始终按行范数对矩阵进行排序,看看它们是否不同。若两行具有相同的范数,则必须检查具有相同范数的行的排列。但这将问题简化为只有具有相同范数的行。在许多情况下,你可以先按2-范数排序,然后按1-范数排序,最后强行对剩余的排列进行排序。

import numpy as np
def get_row_norm(a):
"""
Sort by 2-norm
"""
row_norms = np.sum(a**2, axis=1)
return row_norms
def sort(a):
"""
Return the matrix a sorted by 2-norm
"""
n = a.shape[0]
# Get the norms
row_norms = get_row_norm(a)
# Get the order
order = np.argsort(row_norms)[::-1]
sorted_a = a.copy()
for m in range(n):
i = order[m]
for k in range(m+1): 
j = order[k]
sorted_a[m, k] = a[i, j]
sorted_a[k, m] = a[i, j]
return sorted_a

a = np.array([
#1 #2 #3 #4 #5 #6 #7
[0, 1, 1, 0, 0, 0, 1], #1
[1, 0, 1, 2, 1, 1, 2], #2
[1, 1, 0, 0, 0, 0, 1], #3
[0, 2, 0, 0, 4, 0, 4], #4
[0, 1, 0, 4, 0, 1, 2], #5
[0, 1, 0, 0, 1, 0, 0], #6
[1, 2, 1, 4, 2, 0, 0]  #7
])  
b = np.array([
#5 #7 #1,3#3,1#2 #4 #6 
[0, 2, 0, 0, 1, 4, 1], #5
[2, 0, 1, 1, 2, 4, 0], #7
[0, 1, 0, 1, 1, 0, 0], #1,3 could be either
[0, 1, 1, 0, 1, 0, 0], #1,3 could be either
[1, 2, 1, 1, 0, 2, 1], #2
[4, 4, 0, 0, 2, 0, 0], #4
[1, 0, 0, 0, 1, 0, 0]  #6
])
# Sort a and b
A = sort(a)
B = sort(b)
# Print the norms
print(get_row_norm(a)) # [ 3. 12.  3. 36. 22.  2. 26.]
print(get_row_norm(A)) # [36. 26. 22. 12.  3.  3.  2.]
print(get_row_norm(B)) # [36. 26. 22. 12.  3.  3.  2.]
# Assert that they are equal
print( (A == B).all())

注意,若它们不相等,你们仍然需要检查第五行和第六行的排列,因为它们的范数是相等的。

最新更新