如何使用numpy.correlate进行自相关?


106

我需要对一组数字进行自相关,据我了解,这只是一组与自身之间的相关性。

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

因此,这个问题实际上是两个问题:

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

另请参阅:stackoverflow.com/questions/12269834/…以获取有关归一化自相关的信息。
amcnabb

Answers:


113

要回答您的第一个问题,numpy.correlate(a, v, mode)是对进行反卷积av并给出指定模式限制的结果。的卷积的定义,C(T)=&Sigma; -∞<I <∞一个 v 吨+ I其中-∞<T <∞,允许从结果-∞〜∞,但显然不能存储无限长的数组。因此必须对其进行裁剪,这就是该模式的用处。共有3种不同的模式:完全,相同和有效:

  • “全”模式返回结果为每一个t地方都av有一定的重叠。
  • “相同”模式返回的结果与最短向量(av)的长度相同。
  • 仅当av完全重叠时,“有效”模式才返回结果。该文件numpy.convolve提供了有关模式的更多细节。

关于第二个问题,我想numpy.correlate 是在给您自相关,也给您更多的相关性。自相关用于确定在某个时间差处信号或功能与自身的相似程度。在时间差为0时,自相关应该是最高的,因为信号与其自身相同,因此您希望自相关结果数组中的第一个元素最大。但是,相关不是在时间差为0时开始的。它以负的时间差开始,接近0,然后变为正值。也就是说,您期望:

自相关(A)=&Sigma; -∞<I <∞一个 v 吨+ I其中0 <= T <∞

但是您得到的是:

自相关(A)=&Sigma; -∞<I <∞一个 v 吨+ I其中-∞<T <∞

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

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

当然,您将需要进行错误检查以确保它x实际上是一维数组。另外,这种解释可能并不是最严格的数学解释。我一直在讨论无限性,因为卷积的定义使用了无限性,但这不一定适用于自相关。因此,这种解释的理论部分可能有点儿怪异,但希望实际结果会有所帮助。这些 有关自相关的页面非常有用,如果您不介意使用符号和繁琐的概念,可以为您提供更好的理论背景。


6
在当前的numpy版本中,可以指定模式“ same”以完全实现A. Levy提出的功能。然后,该函数的主体将显示为:return numpy.correlate(x, x, mode='same')
David Zwicker

13
@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])}看到第一个项目最大的项目
开发人员

19
请注意,此答案给出了非标准化的自相关。
amcnabb

4
我认为@Developer提供正确的切片:[len(x)-1:]从0延迟开始。由于full模式给出结果的大小2*len(x)-1,因此A.Levy [result.size/2:]等于[len(x)-1:]。最好将其设置为int,例如[result.size//2:]
杰森

我发现它必须是int,至少在python 3.7中
kevinkayaks

25

自相关有两个版本:统计和卷积。它们都做相同的事情,只是有一点点细节:统计版本被标准化为间隔[-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)])

9
您想要numpy.corrcoef[x:-i], x[i:])[0,1]在第二行中,因为的返回值为corrcoef2x2矩阵
luispedro

统计和卷积自相关之间有什么区别?
丹尼尔(Daniel)说恢复莫妮卡(Monica)

1
@DanielPendergast:第二句话回答:除了一点点细节外,它们都做同样的事情:前一个[统计]被归一化为区间[-1,1]
n1k31t4

21

使用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
丹尼尔说莫妮卡(Monica)恢复

@DanielPendergast我不太熟悉信号处理。来自numpy文档:“返回Pearson乘积矩相关系数。”。那是信号处理版本吗?
拉蒙·J·罗梅罗与维吉尔18'Jan

18

我认为有两件事使该主题更加混乱:

  1. 统计与信号处理的定义:正如其他人指出的那样,在统计中,我们将自相关归一化为[-1,1]。
  2. 部分与非部分均值/方差:当时间序列在滞后> 0时移动时,它们的重叠大小将始终<原始长度。我们使用原始(非局部)的均值和标准差,还是始终使用不断变化的重叠(局部)计算新的均值和标准差有所不同。(可能对此有一个正式的术语,但现在我要使用“部分”)。

我创建了5个函数来计算1d数组的自相关,具有部分与非部分的区别。一些使用统计中的公式,一些使用在信号处理意义上的相关性,这也可以通过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.correlateFFT)的计算不会为每个重叠计算出不同的均值/标准差。

另请注意,fft, no padding, non-partial(红线)结果是不同的,因为在执行FFT之前,时间序列未填充0s,因此是循环FFT。我无法详细解释原因,这就是我从其他地方学到的。


12

当我遇到相同的问题时,我想与您分享几行代码。实际上,到目前为止,关于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()

如您所见,我已经用正弦曲线和均匀的随机分布对其进行了测试,两个结果看起来都与我期望的一样。请注意,我mode="same"代替mode="full"其他人使用了。


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

@pindakaas您能更具体一点吗?请提供有关您发现的差异以及哪些功能的信息。
Ruggero

为什么不使用p = np.abs(f)
dylnan '18

@dylnan这将给出f的组件的模块,而在这里我们想要一个包含f的组件的方形模块的向量。
Ruggero

1
是的,但是您是否意识到进行列表理解可能会更慢。
杰森

2

我是一名计算生物学家,当我不得不计算几个随机过程的时间序列之间的自相关/互相关性时,我意识到自己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

在我看来,以前的答案都没有涉及这种自相关/互相关的情况:希望这个答案对像我这样从事随机过程的人可能有用。


1

我使用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)

1

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

时间复杂度为 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提供的方法有效,但是我在PC上对其进行了测试,其时间复杂度似乎为N * N

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

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=针对特定应用程序的选项进行调整。该页面底部提供了该功能背后的统计信息


0

我认为对OP问题的真正答案简明地包含在Numpy.correlate文档的以下摘录中:

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

这意味着,当不使用'mode'定义时,Numpy.correlate函数在为其两个输入参数赋予相同的矢量时(即-用于执行自相关时)将返回标量。


0

一个没有熊猫的简单解决方案:

import numpy as np

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

0

给定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)

为什么autocorrelation_plot()在这种情况下不使用?(参见stats.stackexchange.com/questions/357300/...
Qaswed
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.