【问题标题】:Why NUMPY correlate and corrcoef return different values and how to "normalize" a correlate in "full" mode?为什么 NUMPY correlate 和 corrcoef 返回不同的值以及如何在“完整”模式下“标准化”相关?
【发布时间】:2011-08-04 02:25:36
【问题描述】:

我正在尝试在 Python 中使用 Numpy 进行时间序列分析。

我有两个中等大小的系列,每个都有 20k 值,我想检查滑动相关性。

corrcoef 给了我一个自相关/相关系数矩阵作为输出。在我的情况下,它本身没有任何用处,因为其中一个系列包含滞后。

correlate 函数(在 mode="full" 中)返回一个 40k 元素列表,该列表看起来确实像我想要的结果(峰值与列表中心一样远,因为 Lag 表示),但这些值都很奇怪 - 最多 500,而我期待的是从 -1 到 1 的值。

我不能把它全部除以最大值;我知道最大相关性不是 1。

如何标准化“互相关”(“完整”模式下的相关),以便返回值将是每个滞后步骤的相关性,而不是那些非常大、奇怪的值?

【问题讨论】:

    标签: python numpy normalization time-series correlation


    【解决方案1】:

    您正在寻找归一化的互相关。此选项在 Numpy 中尚不可用,但 a patch 正在等待审核,这正是您想要的。我认为应用它应该不会太难。大多数补丁只是文档字符串的东西。它添加的唯一代码行是

    if normalize:
        a = (a - mean(a)) / (std(a) * len(a))
        v = (v - mean(v)) /  std(v)
    

    其中 a 和 v 是输入的 numpy 数组,您将在其中找到互相关。将它们添加到您自己的 Numpy 发行版中或者只是制作相关函数的副本并在那里添加行应该不难。如果我选择走这条路,我会亲自做后者。

    另一个可能更好的替代方法是在将输入向量发送到相关之前对其进行归一化。由您自己决定。

    顺便说一句,除了除以len(a) 而不是(len(a)-1) 之外,这似乎是根据Wikipedia page on cross-correlation 的正确规范化。我觉得这种差异类似于standard deviation of the sample vs. sample standard deviation,在我看来真的不会有太大的不同。

    【讨论】:

    • 如果有人在寻找它,补丁(仍在等待中)现在位于github
    • 顺便说一句...“除以 len(a)”返回 1.0 的自相关(零滞后)是正确的。除以len(a)-1 在我的测试中返回稍大的值(带有高斯噪声)。
    • 仅供参考,MATLAB 中的函数xcorr 在使用 scaleopt='coeff' 时会进行另一次归一化,即xcorr(a, b, 'coeff') = xcorr(a, b) / (norm(a) * norm(b))。更多详情请见stackoverflow.com/a/39151702/5392420
    • @Justin - 你为什么要通过除以len(a) 来规范化a 而不要为v 这样做?
    • @Matteo 您可以在 a 或 v 中通过 len(a) 进行标准化,但不能同时在两者中。此外,您可以通过len(a) 的平方根来标准化每个。如果您查看有关互相关的维基百科页面,则在零归一化互相关等式前面只有一个因子 len(a)
    【解决方案2】:

    根据这个slides,我建议这样做:

    def cross_correlation(a1, a2):
            lags = range(-len(a1)+1, len(a2))
            cs = []
            for lag in lags:
                idx_lower_a1 = max(lag, 0)
                idx_lower_a2 = max(-lag, 0)
                idx_upper_a1 = min(len(a1), len(a1)+lag)
                idx_upper_a2 = min(len(a2), len(a2)-lag)
                b1 = a1[idx_lower_a1:idx_upper_a1]
                b2 = a2[idx_lower_a2:idx_upper_a2]
                c = np.correlate(b1, b2)[0]
                c = c / np.sqrt((b1**2).sum() * (b2**2).sum())
                cs.append(c)
            return cs
    

    【讨论】:

      【解决方案3】:

      对于full 模式,直接在滞后信号/特征上计算corrcoef 是否有意义?代码

      from dataclasses import dataclass
      from typing import Any, Optional, Sequence
      
      import numpy as np
      
      ArrayLike = Any
      
      
      @dataclass
      class XCorr:
          cross_correlation: np.ndarray
          lags: np.ndarray
      
      
      def cross_correlation(
          signal: ArrayLike, feature: ArrayLike, lags: Optional[Sequence[int]] = None
      ) -> XCorr:
          """
          Computes normalized cross correlation between the `signal` and the `feature`.
          Current implementation assumes the `feature` can't be longer than the `signal`.
          You can optionally provide specific lags, if not provided `signal` is padded
          with the length of the `feature` - 1, and the `feature` is slid/padded (creating lags)
          with 0 padding to match the length of the new signal. Pearson product-moment
          correlation coefficients is computed for each lag.
      
          See: https://en.wikipedia.org/wiki/Cross-correlation
      
          :param signal: observed signal
          :param feature: feature you are looking for
          :param lags: optional lags, if not provided equals to (-len(feature), len(signal))
          """
          signal_ar = np.asarray(signal)
          feature_ar = np.asarray(feature)
          if np.count_nonzero(feature_ar) == 0:
              raise ValueError("Unsupported - feature contains only zeros")
          assert (
              signal_ar.ndim == feature_ar.ndim == 1
          ), "Unsupported - only 1d signal/feature supported"
          assert len(feature_ar) <= len(
              signal
          ), "Unsupported - signal should be at least as long as the feature"
          padding_sz = len(feature_ar) - 1
          padded_signal = np.pad(
              signal_ar, (padding_sz, padding_sz), "constant", constant_values=0
          )
          lags = lags if lags is not None else range(-padding_sz, len(signal_ar), 1)
          if np.max(lags) >= len(signal_ar):
              raise ValueError("max positive lag must be shorter than the signal")
          if np.min(lags) <= -len(feature_ar):
              raise ValueError("max negative lag can't be longer than the feature")
          assert np.max(lags) < len(signal_ar), ""
          lagged_patterns = np.asarray(
              [
                  np.pad(
                      feature_ar,
                      (padding_sz + lag, len(signal_ar) - lag - 1),
                      "constant",
                      constant_values=0,
                  )
                  for lag in lags
              ]
          )
          return XCorr(
              cross_correlation=np.corrcoef(padded_signal, lagged_patterns)[0, 1:],
              lags=np.asarray(lags),
          )
      

      例子:

      signal = [0, 0, 1, 0.5, 1, 0, 0, 1]
      feature = [1, 0, 0, 1]
      xcorr = cross_correlation(signal, feature)
      assert xcorr.lags[xcorr.cross_correlation.argmax()] == 4
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-07-15
        • 2013-01-11
        • 2011-09-28
        相关资源
        最近更新 更多