【问题标题】:Measuring the similarity between two irregular plots测量两个不规则图之间的相似性
【发布时间】:2015-12-12 13:49:06
【问题描述】:

我有两条不规则的线作为 [x,y] 坐标的列表,其中有波峰和波谷。列表的长度可能略有不同(不相等)。我想测量它们的相似性,以便检查峰和谷(具有相似深度或高度)的出现是否以适当的间隔出现并给出相似性度量。我想在 Python 中做到这一点。有没有内置函数可以做到这一点?

【问题讨论】:

  • 有什么相似之处;您在寻找精确匹配吗?点列表是否必须按顺序排列,还是可以重新排序?
  • 不精确但模式匹配测量。它必须有序
  • 您的数据点在 x 轴上的间距是否相等?
  • 我已经发布了两张有问题的图片...你能看到其中的某种模式吗?如何衡量这种模式相似性?

标签: python matplotlib signal-processing correlation cross-correlation


【解决方案1】:

我不知道 Python 中有任何内置函数可以做到这一点。

我可以为您提供 Python 生态系统中您可以使用的可能功能的列表。这绝不是函数的完整列表,可能还有很多我不知道的方法。

如果数据是有序的,但你不知道哪个数据点是第一个,哪个数据点是最后一个:

  1. 使用有向 Hausdorff 距离

如果数据是有序的,并且你知道第一个和最后一个点是正确的:

  1. 离散 Fréchet 距离 *
  2. 动态时间规整 (DTW) *
  3. 部分曲线映射 (PCM) **
  4. 曲线长度距离度量(使用从开始到结束的弧长距离)**
  5. 两条曲线之间的面积**

* 用于各种机器学习任务的一般数学方法

**我用来识别独特材料滞后响应的方法

首先让我们假设我们有两个完全相同的随机 X Y 数据。请注意,所有这些方法都将返回零。如果没有,可以从 pip 安装相似度度量。

import numpy as np
from scipy.spatial.distance import directed_hausdorff
import similaritymeasures
import matplotlib.pyplot as plt

# Generate random experimental data
np.random.seed(121)
x = np.random.random(100)
y = np.random.random(100)
P = np.array([x, y]).T

# Generate an exact copy of P, Q, which we will use to compare
Q = P.copy()

dh, ind1, ind2 = directed_hausdorff(P, Q)
df = similaritymeasures.frechet_dist(P, Q)
dtw, d = similaritymeasures.dtw(P, Q)
pcm = similaritymeasures.pcm(P, Q)
area = similaritymeasures.area_between_two_curves(P, Q)
cl = similaritymeasures.curve_length_measure(P, Q)

# all methods will return 0.0 when P and Q are the same
print(dh, df, dtw, pcm, cl, area)

打印输出是 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 这是因为曲线P和Q完全一样!

现在让我们假设 P 和 Q 不同。

# Generate random experimental data
np.random.seed(121)
x = np.random.random(100)
y = np.random.random(100)
P = np.array([x, y]).T

# Generate random Q
x = np.random.random(100)
y = np.random.random(100)
Q = np.array([x, y]).T

dh, ind1, ind2 = directed_hausdorff(P, Q)
df = similaritymeasures.frechet_dist(P, Q)
dtw, d = similaritymeasures.dtw(P, Q)
pcm = similaritymeasures.pcm(P, Q)
area = similaritymeasures.area_between_two_curves(P, Q)
cl = similaritymeasures.curve_length_measure(P, Q)

# all methods will return 0.0 when P and Q are the same
print(dh, df, dtw, pcm, cl, area)

打印输出是 0.107、0.743、37.69、21.5、6.86、11.8 根据每种方法量化 P 与 Q 的差异。

您现在有很多方法可以比较两条曲线。我将从 DTW 开始,因为它已在许多时间序列应用程序中使用,看起来像您上传的数据。

我们可以用下面的代码可视化 P 和 Q 的样子。

plt.figure()
plt.plot(P[:, 0], P[:, 1])
plt.plot(Q[:, 0], Q[:, 1])
plt.show()

【讨论】:

    【解决方案2】:

    由于您的数组大小不同(并且我假设您使用相同的实时时间),您需要对它们进行插值以比较相关的点集。 以下代码执行此操作,并计算相关性度量:

    #!/usr/bin/python
    import numpy as np
    from scipy.interpolate import interp1d
    import matplotlib.pyplot as plt
    import scipy.spatial.distance as ssd 
    import scipy.stats as ss
    
    x = np.linspace(0, 10, num=11)
    x2 = np.linspace(1, 11, num=13)
    
    y = 2*np.cos( x) + 4 + np.random.random(len(x))
    y2 = 2* np.cos(x2) + 5 + np.random.random(len(x2))
    
    # Interpolating now, using linear, but you can do better based on your data
    f = interp1d(x, y)
    f2 = interp1d(x2,y2)
    
    points = 15
    
    xnew = np.linspace ( min(x), max(x), num = points) 
    xnew2 = np.linspace ( min(x2), max(x2), num = points) 
    
    ynew = f(xnew) 
    ynew2 = f2(xnew2) 
    plt.plot(x,y, 'r', x2, y2, 'g', xnew, ynew, 'r--', xnew2, ynew2, 'g--')
    plt.show()
    
    # Now compute correlations
    print ssd.correlation(ynew, ynew2) # Computes a distance measure based on correlation between the two vectors
    print np.correlate(ynew, ynew2, mode='valid') # Does a cross-correlation of same sized arrays and gives back correlation
    print np.corrcoef(ynew, ynew2) # Gives back the correlation matrix for the two arrays
    
    print ss.spearmanr(ynew, ynew2) # Gives the spearman correlation for the two arrays
    

    输出:

    0.499028272458
    
    [ 363.48984942]
    
    [[ 1.          0.50097173]
     [ 0.50097173  1.        ]]
    
    SpearmanrResult(correlation=0.45357142857142857, pvalue=0.089485900143027278)
    

    请记住,此处的相关性是参数和 pearson 类型,并假定计算相关性是单调性的。如果不是这种情况,并且您认为您的数组只是一起更改符号,则可以使用 Spearman 的相关性,如上一个示例所示。

    【讨论】:

      【解决方案3】:

      我不知道内置函数,但听起来你可以修改Levenshtein's distance。以下代码取自wikibooks的代码。

      def point_distance(p1, p2):
          # Define distance, if they are the same, then the distance should be 0
      
      
      def levenshtein_point(l1, l2):
          if len(l1) < len(l2):
              return levenshtein(l2, l1)
      
          # len(l1) >= len(l2)
          if len(l2) == 0:
              return len(l1)
      
          previous_row = range(len(l2) + 1)
          for i, p1 in enumerate(l1):
              current_row = [i + 1]
              for j, p2 in enumerate(l2):
                  print('{},{}'.format(p1, p2))
                  insertions = previous_row[j + 1] + 1 # j+1 instead of j since previous_row and current_row are one character longer
                  deletions = current_row[j] + 1       # than l2
                  substitutions = previous_row[j] + point_distance(p1, p2)
                  current_row.append(min(insertions, deletions, substitutions))
              previous_row = current_row
      
          return previous_row[-1]
      

      【讨论】: