通过生成互相关矩阵来测试几个数据集的相似性

时间:2017-03-17 01:47:07

标签: python scipy

我试图比较几个数据集并基本上测试,如果它们显示相同的功能,尽管此功能可能会移位,反转或衰减。 下面是一个非常简单的例子:

A = np.array([0., 0, 0, 1., 2., 3., 4., 3, 2, 1, 0, 0, 0])
B = np.array([0., 0, 0, 0, 0, 1, 2., 3., 4, 3, 2, 1, 0])
C = np.array([0., 0, 0, 1, 1.5, 2, 1.5, 1, 0, 0, 0, 0, 0])
D = np.array([0., 0, 0, 0, 0, -2, -4, -2, 0, 0, 0, 0, 0])
x = np.arange(0,len(A),1)

That look like this

我认为最好的方法是将这些信号归一化并得到绝对值(在这个阶段它们的衰减对我来说并不重要,我对这个位置很感兴趣......但我可能错了,所以我也欢迎对这个概念的想法)并计算它们重叠的区域。我正在跟进this answer - 解决方案看起来非常优雅和简单,但我可能会错误地实施它。

def normalize(sig):
    #ns = sig/max(np.abs(sig))
    ns = sig/sum(sig)
    return ns
a = normalize(A)
b = normalize(B)
c = normalize(C)
d = normalize(D)

然后看起来像这样: ![Normalized

但是,当我尝试从答案中实现解决方案时,我遇到了问题。

OLD

for c1,w1 in enumerate([a,b,c,d]):
    for c2,w2 in enumerate([a,b,c,d]):
        w1 = np.abs(w1)
        w2 = np.abs(w2)
        M[c1,c2] = integrate.trapz(min(np.abs(w2).any(),np.abs(w1).any()))
print M

制作TypeError: 'numpy.bool_' object is not iterableIndexError: list assignment index out of range。但我只包含了.any(),因为没有它们,我就会得到ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

编辑 - 新 (感谢@Kody King)

现在新代码:

M = np.zeros([4,4])
SH = np.zeros([4,4])
for c1,w1 in enumerate([a,b,c,d]):
    for c2,w2 in enumerate([a,b,c,d]):
        crossCorrelation = np.correlate(w1,w2, 'full')
        bestShift = np.argmax(crossCorrelation)

        # This reverses the effect of the padding.
        actualShift = bestShift - len(w2) + 1
        similarity = crossCorrelation[bestShift]

        M[c1,c2] = similarity
        SH[c1,c2] = actualShift
M = M/M.max()
print M, '\n', SH

输出:

[[ 1.          1.          0.95454545  0.63636364]
 [ 1.          1.          0.95454545  0.63636364]
 [ 0.95454545  0.95454545  0.95454545  0.63636364]
 [ 0.63636364  0.63636364  0.63636364  0.54545455]] 
[[ 0. -2.  1.  0.]
 [ 2.  0.  3.  2.]
 [-1. -3.  0. -1.]
 [ 0. -2.  1.  0.]]

现在轮班矩阵看起来不错,但实际的相关矩阵却没有。我真的很困惑,因为最低的相关值是将d与自身相关联。我现在想要达到的目的是:

编辑 - 更新

根据建议,我使用了推荐的归一化公式(将信号除以其总和),但问题没有解决,只是反过来。现在d与d的相关性为1,但所有其他信号与它们自身并不相关。

新输出:

[[ 0.45833333  0.45833333  0.5         0.58333333]
 [ 0.45833333  0.45833333  0.5         0.58333333]
 [ 0.5         0.5         0.57142857  0.66666667]
 [ 0.58333333  0.58333333  0.66666667  1.        ]] 
[[ 0. -2.  1.  0.]
 [ 2.  0.  3.  2.]
 [-1. -3.  0. -1.]
 [ 0. -2.  1.  0.]]
  1. 相关值应该最高,以便将信号与自身相关联(即在主对角线上具有最高值)。
  2. 要获得介于0和1之间的相关值,因此,我会在主对角线上有1,而在其他地方有其他数字(0.x)。
  3. 我希望M = M / M.max()能完成这项工作,但前提是条件不是。完成了1,它目前不是。

2 个答案:

答案 0 :(得分:1)

正如ssm所说,numpy的相关功能可以很好地解决这个问题。你提到你对这个职位感兴趣。 correlate函数还可以帮助您判断一个序列与另一个序列的距离。

import numpy as np

def compare(a, b):
    # 'full' pads the sequences with 0's so they are correlated
    # with as little as 1 actual element overlapping.
    crossCorrelation = np.correlate(a,b, 'full')
    bestShift = np.argmax(crossCorrelation)

    # This reverses the effect of the padding.
    actualShift = bestShift - len(b) + 1
    similarity = crossCorrelation[bestShift]

    print('Shift: ' + str(actualShift))
    print('Similatiy: ' + str(similarity))
    return {'shift': actualShift, 'similarity': similarity}

print('\nExpected shift: 0')
compare([0,0,1,0,0], [0,0,1,0,0])
print('\nExpected shift: 2')
compare([0,0,1,0,0], [1,0,0,0,0])
print('\nExpected shift: -2')
compare([1,0,0,0,0], [0,0,1,0,0])

修改

您需要在关联每个序列之前对其进行标准化,否则较大的序列将与所有其他序列具有非常高的相关性。

互相关的属性是:

latex

因此,如果您通过将每个序列除以它的总和进行归一化,则相似性将始终在0和1之间。

我建议你不要取序列的绝对值。这会改变形状,而不仅仅是尺度。例如np.abs([1,-2])== [1,2]。规范化已经确保序列大部分为正,并且最多为1。

第二次修改:

我意识到了。将信号视为矢量。归一化向量始终具有最大点积。互相关只是在不同班次计算的点积。如果你将信号标准化,就像你的矢量(除以sqrt(s点s)),自相关总是最大的和1。

import numpy as np

def normalize(s):
    magSquared = np.correlate(s, s) # s dot itself
    return s / np.sqrt(magSquared)

a = np.array([0., 0, 0, 1., 2., 3., 4., 3, 2, 1, 0, 0, 0])
b = np.array([0., 0, 0, 0, 0, 1, 2., 3., 4, 3, 2, 1, 0])
c = np.array([0., 0, 0, 1, 1.5, 2, 1.5, 1, 0, 0, 0, 0, 0])
d = np.array([0., 0, 0, 0, 0, -2, -4, -2, 0, 0, 0, 0, 0])

a = normalize(a)
b = normalize(b)
c = normalize(c)
d = normalize(d)

M = np.zeros([4,4])
SH = np.zeros([4,4])
for c1,w1 in enumerate([a,b,c,d]):
    for c2,w2 in enumerate([a,b,c,d]):
        # Taking the absolute value catches signals which are flipped.
        crossCorrelation = np.abs(np.correlate(w1, w2, 'full'))
        bestShift = np.argmax(crossCorrelation)

        # This reverses the effect of the padding.
        actualShift = bestShift - len(w2) + 1
        similarity = crossCorrelation[bestShift]

        M[c1,c2] = similarity
        SH[c1,c2] = actualShift
print(M, '\n', SH)

输出:

[[ 1.          1.          0.97700842  0.86164044]
[ 1.          1.          0.97700842  0.86164044]
[ 0.97700842  0.97700842  1.          0.8819171 ]
[ 0.86164044  0.86164044  0.8819171   1.        ]]
[[ 0. -2.  1.  0.]
[ 2.  0.  3.  2.]
[-1. -3.  0. -1.]
[ 0. -2.  1.  0.]]

答案 1 :(得分:0)

您想在矢量之间使用互相关:

例如:

gcc/g++ 4.9.2

如果你不关心这个标志,你可以简单地取绝对值...