压缩矩阵函数以查找配对

11 投票
7 回答
3440 浏览
提问于 2025-04-16 13:48

对于一组观察数据:

[a1,a2,a3,a4,a5]

它们之间的成对距离

d=[[0,a12,a13,a14,a15]
   [a21,0,a23,a24,a25]
   [a31,a32,0,a34,a35]
   [a41,a42,a43,0,a45]
   [a51,a52,a53,a54,0]]

以压缩矩阵的形式给出(上三角部分,来自 scipy.spatial.distance.pdist 的计算):

c=[a12,a13,a14,a15,a23,a24,a25,a34,a35,a45]

问题是,既然我已经有了压缩矩阵中的索引,是否有一个函数(最好是用python写的)f 可以快速告诉我是哪两个观察数据被用来计算这些距离的?

f(c,0)=(1,2)
f(c,5)=(2,4)
f(c,9)=(4,5)
...

我尝试了一些解决方案,但没有一个值得一提的 :(

7 个回答

2

很明显,你要找的这个函数 f 需要一个第二个参数:矩阵的维度——在你的例子中,就是 5。

第一次尝试:

def f(dim,i): 
  d = dim-1 ; s = d
  while i<s: 
    s+=d ; d-=1
  return (dim-d, i-s+d)
29

压缩矩阵的索引公式是

index = d * (d - 1) / 2 - (d - i) * (d - i - 1) / 2 + j - i - 1

这里的 i 是行索引,j 是列索引,而 d 是原始上三角矩阵的行长度(d X d)。

假设这个索引指的是原始矩阵某一行中最左边的非零元素。对于所有最左边的索引,

j == i + 1

所以

index = d * (d - 1) / 2 - (d - i) * (d - i - 1) / 2 + i + 1 - i - 1
index = d * (d - 1) / 2 - (d - i) * (d - i - 1) / 2

通过一些代数运算,我们可以把这个公式改写为

i ** 2 + (1 - (2 * d)) * i + 2 * index == 0

接着,我们可以用二次公式来找出这个方程的根,而我们只关心正根。

如果这个索引确实对应于最左边的非零单元格,那么我们会得到一个正整数作为解,这个解对应于行号。然后,找到列号就只是简单的算术运算了。

j = index - d * (d - 1) / 2 + (d - i) * (d - i - 1)/ 2 + i + 1

如果这个索引不对应于最左边的非零单元格,那么我们就找不到整数根,但可以取正根的下限作为行号。

def row_col_from_condensed_index(d,index):
    b = 1 - (2 * d) 
    i = (-b - math.sqrt(b ** 2 - 8 * index)) // 2
    j = index + i * (b + i + 2) // 2 + 1
    return (i,j)  

如果你不知道 d 的值,可以通过压缩矩阵的长度来计算出来。

((d - 1) * d) / 2 == len(condensed_matrix)
d = (1 + math.sqrt(1 + 8 * len(condensed_matrix))) // 2 
4

你可能会觉得 triu_indices 这个函数很有用。比如说,

In []: ti= triu_indices(5, 1)
In []: r, c= ti[0][5], ti[1][5]
In []: r, c
Out[]: (1, 3)

要注意的是,索引是从0开始的。你可以根据自己的需要进行调整,比如:

In []: def f(n, c):
   ..:     n= ceil(sqrt(2* n))
   ..:     ti= triu_indices(n, 1)
   ..:     return ti[0][c]+ 1, ti[1][c]+ 1
   ..:
In []: f(len(c), 5)
Out[]: (2, 4)

撰写回答