【问题标题】:How can I use numpy.correlate to do autocorrelation?如何使用 numpy.correlate 进行自相关?
【发布时间】:2010-10-13 05:21:13
【问题描述】:

我需要对一组数字进行自相关,据我所知,这只是该组与自身的相关。

我已经尝试过使用 numpy 的相关函数,但我不相信结果,因为它几乎总是给出第一个数字 不是 最大的向量,因为它应该是.

所以,这个问题其实是两个问题:

  1. numpy.correlate 到底在做什么?
  2. 如何使用它(或其他东西)进行自相关?

【问题讨论】:

标签: python math numpy numerical-methods


【解决方案1】:

statsmodels.tsa.stattools.acf() 中提供了 numpy.correlate 的替代方法。这会产生一个不断减小的自相关函数,就像 OP 所描述的那样。实现起来相当简单:

from statsmodels.tsa import stattools
# x = 1-D array
# Yield normalized autocorrelation function of number lags
autocorr = stattools.acf( x )

# Get autocorrelation coefficient at lag = 1
autocorr_coeff = autocorr[1]

默认行为是在 40 个 nlag 处停止,但这可以通过 nlag= 选项针对您的特定应用进行调整。页面底部有一个statistics behind the function 的引用。

【讨论】:

    【解决方案2】:

    使用numpy.corrcoef 函数而不是numpy.correlate 来计算t 滞后的统计相关性:

    def autocorr(x, t=1):
        return numpy.corrcoef(numpy.array([x[:-t], x[t:]]))
    

    【讨论】:

    • “相关系数”不是指信号处理中使用的自相关,而不是统计中使用的自相关吗? en.wikipedia.org/wiki/Autocorrelation#Signal_processing
    • @DanielPendergast 我对信号处理不太熟悉。来自 numpy 文档:“返回 Pearson 积矩相关系数。”。那是信号处理版本吗?
    【解决方案3】:

    我是一名计算生物学家,当我必须计算随机过程的几个时间序列之间的自/互相关时,我意识到 np.correlate 并没有做我需要的工作。

    确实,np.correlate 似乎缺少的是 在距离 ? 处所有可能的时间点对的平均值

    这是我如何定义一个函数来做我需要的:

    def autocross(x, y):
        c = np.correlate(x, y, "same")
        v = [c[i]/( len(x)-abs( i - (len(x)/2)  ) ) for i in range(len(c))]
        return v
    

    在我看来,以前的答案都没有涵盖这个自/互相关的实例:希望这个答案对像我这样从事随机过程的人有用。

    【讨论】:

      【解决方案4】:

      自相关有两个版本:统计和卷积。他们都做同样的事情,除了一点细节:统计版本被标准化为区间 [-1,1]。以下是您如何进行统计的示例:

      def acf(x, length=20):
          return numpy.array([1]+[numpy.corrcoef(x[:-i], x[i:])[0,1]  \
              for i in range(1, length)])
      

      【讨论】:

      • 你希望numpy.corrcoef[x:-i], x[i:])[0,1] 在第二行,因为corrcoef 的返回值是一个2x2 矩阵
      • 统计自相关和卷积自相关有什么区别?
      • @DanielPendergast:第二句话回答说:他们都做同样的事情,除了一点细节:前者[统计]被归一化为区间[-1 ,1]
      • @DanielsaysReinstateMonica 统计自动相关意味着您可以在某个时间点建立时间序列与先前协变量的统计关系,例如回归等参数:$E(y_t|y_{tk}) = fn(y_{tk}, \beta)$ 和 $Var(y_t|y_{tk}) = f(...) $ 由于期望和方差本质上是统计量,因此得名。卷积 AC 通常用于信号处理,例如平滑/滤波,例如,通过在信号上使用卷积(滑动)窗口(例如 Sin 波),并在每个点进行逐点相乘和相加。
      【解决方案5】:

      绘制给定 pandas datatime 返回系列的统计自相关:

      import matplotlib.pyplot as plt
      
      def plot_autocorr(returns, lags):
          autocorrelation = []
          for lag in range(lags+1):
              corr_lag = returns.corr(returns.shift(-lag)) 
              autocorrelation.append(corr_lag)
          plt.plot(range(lags+1), autocorrelation, '--o')
          plt.xticks(range(lags+1))
          return np.array(autocorrelation)
      

      【讨论】:

      【解决方案6】:

      使用傅里叶变换和卷积定理

      时间复杂度为N*log(N)

      def autocorr1(x):
          r2=np.fft.ifft(np.abs(np.fft.fft(x))**2).real
          return r2[:len(x)//2]
      

      这里是归一化无偏的版本,也是N*log(N)

      def autocorr2(x):
          r2=np.fft.ifft(np.abs(np.fft.fft(x))**2).real
          c=(r2/x.shape-np.mean(x)**2)/np.std(x)**2
          return c[:len(x)//2]
      

      A. Levy 提供的方法可行,但我在我的电脑上测试过,它的时间复杂度似乎是 N*N

      def autocorr(x):
          result = numpy.correlate(x, x, mode='full')
          return result[result.size/2:]
      

      【讨论】:

        【解决方案7】:

        没有 pandas 的简单解决方案:

        import numpy as np
        
        def auto_corrcoef(x):
           return np.corrcoef(x[1:-1], x[2:])[0,1]
        

        【讨论】:

          【解决方案8】:

          我认为有两点让这个话题更加混乱:

          1. 统计对比信号处理定义:正如其他人指出的那样,在统计中,我们将自相关归一化为 [-1,1]。
          2. 部分对比非部分均值/方差:当时间序列以滞后 > 0 移动时,它们的重叠大小将始终

          我创建了 5 个函数来计算一维数组的自相关,其中包含部分 vs.非局部区别。有些使用统计公式,有些使用信号处理意义上的相关,这也可以通过 FFT 完成。但所有结果都是统计数据定义中的自相关,因此它们说明了它们是如何相互关联的。代码如下:

          import numpy
          import matplotlib.pyplot as plt
          
          def autocorr1(x,lags):
              '''numpy.corrcoef, partial'''
          
              corr=[1. if l==0 else numpy.corrcoef(x[l:],x[:-l])[0][1] for l in lags]
              return numpy.array(corr)
          
          def autocorr2(x,lags):
              '''manualy compute, non partial'''
          
              mean=numpy.mean(x)
              var=numpy.var(x)
              xp=x-mean
              corr=[1. if l==0 else numpy.sum(xp[l:]*xp[:-l])/len(x)/var for l in lags]
          
              return numpy.array(corr)
          
          def autocorr3(x,lags):
              '''fft, pad 0s, non partial'''
          
              n=len(x)
              # pad 0s to 2n-1
              ext_size=2*n-1
              # nearest power of 2
              fsize=2**numpy.ceil(numpy.log2(ext_size)).astype('int')
          
              xp=x-numpy.mean(x)
              var=numpy.var(x)
          
              # do fft and ifft
              cf=numpy.fft.fft(xp,fsize)
              sf=cf.conjugate()*cf
              corr=numpy.fft.ifft(sf).real
              corr=corr/var/n
          
              return corr[:len(lags)]
          
          def autocorr4(x,lags):
              '''fft, don't pad 0s, non partial'''
              mean=x.mean()
              var=numpy.var(x)
              xp=x-mean
          
              cf=numpy.fft.fft(xp)
              sf=cf.conjugate()*cf
              corr=numpy.fft.ifft(sf).real/var/len(x)
          
              return corr[:len(lags)]
          
          def autocorr5(x,lags):
              '''numpy.correlate, non partial'''
              mean=x.mean()
              var=numpy.var(x)
              xp=x-mean
              corr=numpy.correlate(xp,xp,'full')[len(x)-1:]/var/len(x)
          
              return corr[:len(lags)]
          
          
          if __name__=='__main__':
          
              y=[28,28,26,19,16,24,26,24,24,29,29,27,31,26,38,23,13,14,28,19,19,\
                      17,22,2,4,5,7,8,14,14,23]
              y=numpy.array(y).astype('float')
          
              lags=range(15)
              fig,ax=plt.subplots()
          
              for funcii, labelii in zip([autocorr1, autocorr2, autocorr3, autocorr4,
                  autocorr5], ['np.corrcoef, partial', 'manual, non-partial',
                      'fft, pad 0s, non-partial', 'fft, no padding, non-partial',
                      'np.correlate, non-partial']):
          
                  cii=funcii(y,lags)
                  print(labelii)
                  print(cii)
                  ax.plot(lags,cii,label=labelii)
          
              ax.set_xlabel('lag')
              ax.set_ylabel('correlation coefficient')
              ax.legend()
              plt.show()
          

          这是输出图:

          我们没有看到所有 5 行,因为其中 3 行重叠(在紫色处)。重叠都是非部分自相关。这是因为信号处理方法(np.correlate,FFT)的计算不会为每个重叠计算不同的均值/标准差。

          还要注意fft, no padding, non-partial(红线)的结果是不同的,因为它在进行 FFT 之前没有用 0 填充时间序列,所以它是循环 FFT。我无法详细解释为什么,这是我从其他地方学到的。

          【讨论】:

            【解决方案9】:

            您的问题 1 已在此处的几个优秀答案中得到广泛讨论。

            我想与您分享几行代码,让您仅根据自相关的数学属性计算信号的自相关。也就是说,可以通过以下方式计算自相关:

            1. 从信号中减去均值,得到无偏信号

            2. 计算无偏信号的傅里叶变换

            3. 通过对无偏信号的傅里叶变换的每个值取平方范数,计算信号的功率谱密度

            4. 计算功率谱密度的傅里叶逆变换

            5. 通过无偏信号的平方和对功率谱密度的傅里叶逆变换进行归一化,只取结果向量的一半

            执行此操作的代码如下:

            def autocorrelation (x) :
                """
                Compute the autocorrelation of the signal, based on the properties of the
                power spectral density of the signal.
                """
                xp = x-np.mean(x)
                f = np.fft.fft(xp)
                p = np.array([np.real(v)**2+np.imag(v)**2 for v in f])
                pi = np.fft.ifft(p)
                return np.real(pi)[:x.size/2]/np.sum(xp**2)
            

            【讨论】:

            • 这可能有问题吗?我无法将它的结果与其他自动相关函数匹配。该函数看起来很相似,但似乎有些挤压。
            • @pindakaas 你能更具体一点吗?请提供信息,说明您发现了哪些差异,以及哪些功能。
            • 为什么不使用p = np.abs(f)
            • @dylnan 这将给出 f 的组件的 modules,而这里我们想要一个包含 f 组件的 square modules 的向量f.
            • 是的,但是您是否意识到进行列表理解可能更慢。
            【解决方案10】:

            我使用 talib.CORREL 进行这样的自相关,我怀疑你可以对其他包做同样的事情:

            def autocorrelate(x, period):
            
                # x is a deep indicator array 
                # period of sample and slices of comparison
            
                # oldest data (period of input array) may be nan; remove it
                x = x[-np.count_nonzero(~np.isnan(x)):]
                # subtract mean to normalize indicator
                x -= np.mean(x)
                # isolate the recent sample to be autocorrelated
                sample = x[-period:]
                # create slices of indicator data
                correls = []
                for n in range((len(x)-1), period, -1):
                    alpha = period + n
                    slices = (x[-alpha:])[:period]
                    # compare each slice to the recent sample
                    correls.append(ta.CORREL(slices, sample, period)[-1])
                # fill in zeros for sample overlap period of recent correlations    
                for n in range(period,0,-1):
                    correls.append(0)
                # oldest data (autocorrelation period) will be nan; remove it
                correls = np.array(correls[-np.count_nonzero(~np.isnan(correls)):])      
            
                return correls
            
            # CORRELATION OF BEST FIT
            # the highest value correlation    
            max_value = np.max(correls)
            # index of the best correlation
            max_index = np.argmax(correls)
            

            【讨论】:

              【解决方案11】:

              为了回答您的第一个问题,numpy.correlate(a, v, mode) 正在执行av 的反相的卷积,并给出按指定模式裁剪的结果。 definition of convolution, C(t)=∑ -∞ aivt+i 其中 -∞

              • “完整”模式返回每个t 的结果,其中av 有一些重叠。
              • “相同”模式返回与最短向量(av)长度相同的结果。
              • “有效”模式仅在av 完全重叠时返回结果。 documentation for numpy.convolve 提供了有关模式的更多详细信息。

              对于你的第二个问题,我认为numpy.correlate 给你自相关,它只是给你一点。自相关用于找出信号或函数在某个时间差与其自身的相似程度。在时间差为 0 时,自相关应该是最高的,因为信号与其自身相同,因此您预计自相关结果数组中的第一个元素将是最大的。但是,相关性并不是从 0 的时间差开始。它从负的时间差开始,接近 0,然后变为正数。也就是说,您期望:

              自相关(a) = ∑ -∞ aivt+i 其中 0

              但你得到的是:

              自相关(a) = ∑ -∞ aivt+i 其中 -∞

              您需要做的是获取相关结果的后半部分,这应该是您正在寻找的自相关。一个简单的python函数可以做到这一点:

              def autocorr(x):
                  result = numpy.correlate(x, x, mode='full')
                  return result[result.size/2:]
              

              当然,您需要进行错误检查以确保 x 实际上是一维数组。此外,这种解释可能不是最严格的数学解释。我一直在抛出无穷大,因为卷积的定义使用它们,但这并不一定适用于自相关。因此,这种解释的理论部分可能有点不可靠,但希望实际结果会有所帮助。 These pages 关于自相关非常有帮助,如果您不介意阅读符号和繁重的概念,可以为您提供更好的理论背景。

              【讨论】:

              • 在当前的 numpy 版本中,可以指定模式“相同”以完全实现 A. Levy 建议的内容。然后函数的主体可以读取return numpy.correlate(x, x, mode='same')
              • @DavidZwicker 但结果不同! np.correlate(x,x,mode='full')[len(x)//2:] != np.correlate(x,x,mode='same')。例如,x = [1,2,3,1,2]; np.correlate(x,x,mode='full'); {>>> array([ 2, 5, 11, 13, 19, 13, 11, 5, 2])} np.correlate(x,x,mode='same'); {>>> array([11, 13, 19, 13, 11])}。正确的是:np.correlate(x,x,mode='full')[len(x)-1:]; {>>> array([19, 13, 11, 5, 2])} 看到第一项最大的一项
              • 请注意,这个答案给出了非标准化的自相关。
              • 我认为@Developer 给出了正确的切片:[len(x)-1:] 从 0-lag 开始。因为full 模式给出了结果大小2*len(x)-1,所以A.Levy 的[result.size/2:][len(x)-1:] 相同。最好将其设为 int,例如 [result.size//2:]
              • 我发现它必须是一个int,至少在python 3.7中
              【解决方案12】:

              我认为 OP 问题的真正答案简洁地包含在 Numpy.correlate 文档的这段摘录中:

              mode : {'valid', 'same', 'full'}, optional
                  Refer to the `convolve` docstring.  Note that the default
                  is `valid`, unlike `convolve`, which uses `full`.
              

              这意味着,在没有“模式”定义的情况下使用时,Numpy.correlate 函数将返回一个标量,同时为其两个输入参数提供相同的向量(即 - 当用于执行自相关时)。

              【讨论】:

                【解决方案13】:

                由于我刚刚遇到了同样的问题,我想与您分享几行代码。事实上,到目前为止,关于 stackoverflow 中自相关的文章有几篇非常相似的文章。如果您将自相关定义为 a(x, L) = sum(k=0,N-L-1)((xk-xbar)*(x(k+L)-xbar))/sum(k=0,N-1)((xk-xbar)**2) [这是 IDL 的 a_correlate 函数中给出的定义,并且它与我在问题 #12269834 的答案 2 中看到的一致],那么以下似乎给出了正确的结果:

                import numpy as np
                import matplotlib.pyplot as plt
                
                # generate some data
                x = np.arange(0.,6.12,0.01)
                y = np.sin(x)
                # y = np.random.uniform(size=300)
                yunbiased = y-np.mean(y)
                ynorm = np.sum(yunbiased**2)
                acor = np.correlate(yunbiased, yunbiased, "same")/ynorm
                # use only second half
                acor = acor[len(acor)/2:]
                
                plt.plot(acor)
                plt.show()
                

                如您所见,我已经使用 sin 曲线和均匀随机分布对此进行了测试,这两个结果看起来都符合我的预期。请注意,我使用mode="same" 而不是mode="full",就像其他人一样。

                【讨论】:

                  猜你喜欢
                  • 2016-09-23
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2020-10-28
                  • 2016-10-30
                  • 1970-01-01
                  • 2021-04-01
                  相关资源
                  最近更新 更多