Otomatik korelasyon yapmak için numpy.correlate'i nasıl kullanabilirim?


107

Bir dizi sayının oto-korelasyonunu yapmam gerekiyor, anladığım kadarıyla bu sadece setin kendisiyle korelasyonu.

Bunu numpy'nin bağıntı işlevini kullanarak denedim, ancak sonuca inanmıyorum, çünkü neredeyse her zaman ilk sayının en büyük olmadığı vektörü olması gerektiği gibi veriyor.

Yani, bu soru gerçekten iki sorudur:

  1. Tam olarak ne numpy.correlateyapıyor?
  2. Oto-korelasyon yapmak için onu (veya başka bir şeyi) nasıl kullanabilirim?

Normalleştirilmiş otokorelasyon hakkında bilgi için ayrıca bkz: stackoverflow.com/questions/12269834/… .
amcnabb

Yanıtlar:


115

İlk sorunuzun cevabı, ' nin tersi ile numpy.correlate(a, v, mode)evrişimini yapmak ve sonuçları belirtilen modda kırpılmış olarak vermektir. Konvolüsyon tanımı , C (t) = Σ -∞ <i <∞ bir ı v t + i burada -∞ <t <∞ gelen -∞ ∞ sonuçları için sağlar, ancak tabii ki, sonsuz uzun bir depolayamıyor dizi. Yani kısaltılması gerekiyor ve mod burada devreye giriyor. 3 farklı mod vardır: tam, aynı ve geçerli:av

  • "tam" mod, her tikisi için de sonuçlar verir ave bir vmiktar çakışır.
  • "aynı" mod, en kısa vektörle ( aveya v) aynı uzunlukta bir sonuç döndürür .
  • "geçerli" mod, yalnızca ave vtamamen birbiriyle örtüştüğünde sonuçları döndürür . Dokümantasyon için numpy.convolvemodları hakkında daha fazla ayrıntı verir.

İkinci soru için, sanırım numpy.correlate olduğunu size otokorelasyon vererek, sadece biraz daha hem sizi veriyor. Otokorelasyon, bir sinyalin veya fonksiyonun belirli bir zaman farkında kendisine ne kadar benzer olduğunu bulmak için kullanılır. 0 zaman farkında, otomatik korelasyon en yüksek olmalıdır çünkü sinyal kendisiyle aynıdır, bu nedenle otokorelasyon sonuç dizisindeki ilk öğenin en büyük olmasını bekliyordunuz. Ancak, korelasyon 0 zaman farkında başlamaz. Negatif bir zaman farkında başlar, 0'a kapanır ve sonra pozitif olur. Yani, bekliyordunuz:

otokorelasyon (a) = ∑ -∞ <i <∞ a i v t + i burada 0 <= t <∞

Ama sahip olduğun şey şuydu:

otokorelasyon (a) = ∑ -∞ <i <∞ a i v t + i burada -∞ <t <∞

Yapmanız gereken şey, korelasyon sonucunuzun son yarısını almaktır ve aradığınız otokorelasyon bu olmalıdır. Bunu yapmak için basit bir python işlevi şöyle olacaktır:

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

Bunun xaslında 1 boyutlu bir dizi olduğundan emin olmak için elbette hata kontrolüne ihtiyacınız olacak . Ayrıca, bu açıklama muhtemelen matematiksel olarak en titiz olanı değil. Evrişim tanımı onları kullandığı için sonsuzlukları atıyorum, ancak bu otokorelasyon için geçerli değil. Bu nedenle, bu açıklamanın teorik kısmı biraz riskli olabilir, ancak umarım pratik sonuçlar yardımcı olur. Otokorelasyon hakkındaki bu sayfalar oldukça yararlıdır ve notasyon ve ağır kavramlar arasında gezinmeyi düşünmezseniz size çok daha iyi bir teorik arka plan sağlayabilir.


6
Mevcut numpy yapılarında, A. Levy'nin önerdiği şeyi tam olarak elde etmek için 'aynı' modu belirlenebilir. Fonksiyonun gövdesi daha sonra okuyabilirreturn numpy.correlate(x, x, mode='same')
David Zwicker

13
@DavidZwicker ama sonuçlar farklı! np.correlate(x,x,mode='full')[len(x)//2:] != np.correlate(x,x,mode='same'). Örneğin, 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])}. Doğru biridir: np.correlate(x,x,mode='full')[len(x)-1:];{ >>> array([19, 13, 11, 5, 2])} bakın ilk öğe olan en büyüğü .
Geliştirici

19
Bu cevabın normalize edilmemiş otokorelasyonu verdiğine dikkat edin.
amcnabb

4
@Developer'ın doğru dilimlemeyi verdiğini düşünüyorum: [len(x)-1:]0-gecikmeden başlar. Çünkü fullmodu sonuç boyutunu verir 2*len(x)-1, A.Levy en [result.size/2:]aynıdır [len(x)-1:]. Bunu bir int yapmak daha iyidir, mesela [result.size//2:].
Jason

En azından python
3.7'de

25

Otomatik korelasyon iki versiyonda gelir: istatistiksel ve evrişim. Küçük bir ayrıntı dışında ikisi de aynı şeyi yapar: İstatistiksel sürüm [-1,1] aralığında olacak şekilde normalleştirilir. İstatistiksel olanı nasıl yaptığınıza dair bir örnek:

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]Dönüş değeri corrcoef2x2 matris olduğu için ikinci satırda olmasını istersiniz
luispedro

İstatistiksel ve evrişimli otokorelasyonlar arasındaki fark nedir?
Daniel, Monica'yı Yeniden

1
@DanielPendergast: İkinci cümle şu yanıtı veriyor: Küçük bir ayrıntı dışında ikisi de aynı şeyi yapıyor: Eski [istatistiksel], [-1,1]
n1k31t4

21

Kullanım numpy.corrcoefyerine işlevini numpy.correlatet bir gecikme için istatistiksel korelasyon hesaplamak için:

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

"Korelasyon katsayıları", istatistiklerde kullanılan otokorelasyona değil sinyal işlemede kullanılan otokorelasyona atıfta bulunmaz mı? en.wikipedia.org/wiki/Autocorrelation#Signal_processing
Daniel, "Reinstate Monica" diyor

@DanielPendergast Sinyal işlemeye aşina değilim. Numpy belgelerinden: "Pearson ürün-moment korelasyon katsayılarını döndürün." Bu sinyal işleme versiyonu mu?
Ramón J Romero y Vigil

18

Bu konuya kafa karıştıran 2 şey olduğunu düşünüyorum:

  1. istatistiksel ve sinyal işleme tanımı: diğerlerinin de işaret ettiği gibi, istatistikte oto-korelasyonu [-1,1] 'e normalleştiriyoruz.
  2. kısmi vs kısmi olmayan ortalama / varyans: zaman serileri> 0 gecikmesinde değiştiğinde, örtüşme boyutları her zaman <orijinal uzunluk olacaktır. Orijinalin (kısmi olmayan) ortalamasını ve std'sini mi kullanıyoruz yoksa her zaman yeni bir ortalama mı hesaplıyoruz ve sürekli değişen örtüşmeyi (kısmi) kullanarak std bir fark yaratıyor. (Muhtemelen bunun için resmi bir terim vardır, ancak şimdilik "kısmi" kullanacağım).

Kısmi ve kısmi olmayan ayrımlarla 1d dizisinin otomatik korelasyonunu hesaplayan 5 işlev oluşturdum. Bazıları istatistiklerden formül kullanır, bazıları ise sinyal işleme anlamında ilişkilendirme kullanır ve bu FFT ile de yapılabilir. Ancak tüm sonuçlar istatistik tanımındaki otomatik korelasyonlardır , bu nedenle birbirleriyle nasıl bağlantılı olduklarını gösterirler. Aşağıdaki kod:

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()

İşte çıktı şekli:

görüntü açıklamasını buraya girin

5 çizginin hepsini görmüyoruz çünkü 3 tanesi üst üste biniyor (mor renkte). Örtüşmelerin tümü kısmi olmayan oto-korelasyonlardır. Bunun nedeni, sinyal işleme yöntemlerinden ( np.correlateFFT) yapılan hesaplamaların her bir örtüşme için farklı bir ortalama / std hesaplamamasıdır.

Ayrıca fft, no padding, non-partial(kırmızı çizgi) sonucunun farklı olduğuna dikkat edin , çünkü FFT yapmadan önce zaman serilerini 0'larla doldurmadı, bu yüzden dairesel FFT. Nedenini ayrıntılı olarak açıklayamam, başka yerden öğrendiğim şey buydu.


12

Az önce aynı problemle karşılaştığım için, sizinle birkaç satır kod paylaşmak istiyorum. Aslında, stackoverflow'da otokorelasyon hakkında şimdiye kadar oldukça benzer birkaç gönderi var. Otokorelasyonu a(x, L) = sum(k=0,N-L-1)((xk-xbar)*(x(k+L)-xbar))/sum(k=0,N-1)((xk-xbar)**2)[IDL'nin a_correlate fonksiyonunda verilen tanımdır ve soru # 12269834'ün 2. cevabında gördüklerime uyuyor ] olarak tanımlarsanız , aşağıdaki doğru sonuçları veriyor gibi görünür:

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()

Gördüğünüz gibi, bunu bir günah eğrisi ve tekdüze bir rastgele dağılımla test ettim ve her iki sonuç da beklediğim gibi görünüyor. Kullandığım unutmayın mode="same"yerine mode="full", diğerleri gibi.


9

1. sorunuz, burada birkaç mükemmel cevapta kapsamlı bir şekilde tartışıldı.

Sadece otokorelasyonun matematiksel özelliklerine dayalı olarak bir sinyalin otokorelasyonunu hesaplamanıza izin veren birkaç satır kodu sizinle paylaşmayı düşündüm. Yani, otokorelasyon şu şekilde hesaplanabilir:

  1. ortalamayı sinyalden çıkarın ve tarafsız bir sinyal elde edin

  2. tarafsız sinyalin Fourier dönüşümünü hesaplayın

  3. Tarafsız sinyalin Fourier dönüşümünün her bir değerinin kare normunu alarak sinyalin güç spektral yoğunluğunu hesaplayın

  4. spektral güç yoğunluğunun ters Fourier dönüşümünü hesaplayın

  5. yansız sinyalin karelerinin toplamı ile güç spektral yoğunluğunun ters Fourier dönüşümünü normalize edin ve elde edilen vektörün sadece yarısını alın

Bunu yapacak kod şudur:

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)

bunda yanlış bir şey olması mümkün mü? Sonuçlarını diğer oto korelasyon işlevleriyle eşleştiremiyorum. İşlev benzer görünüyor, ancak biraz sıkıştırılmış görünüyor.
pindakaas

@pindakaas daha spesifik olabilir misin? lütfen bulduğunuz tutarsızlıklar ve hangi işlevlerle ilgili bilgi veriniz.
Ruggero

Neden kullanmıyorsun p = np.abs(f)?
dylnan

@dylnan Bu , f'nin bileşenlerinin modüllerini verir , oysa biz burada f'nin bileşenlerinin kare modüllerini içeren bir vektör istiyoruz .
Ruggero

1
Evet, ama liste anlamanın muhtemelen daha da yavaş olduğunu fark ettin mi?
Jason

2

Ben bir hesaplamalı biyoloğum ve zaman serisi stokastik süreç çiftleri arasındaki otomatik / çapraz korelasyonları hesaplamam np.correlategerektiğinde bunun ihtiyacım olan işi yapmadığını fark ettim .

Gerçekte, eksik görünen np.correlateşey, 𝜏 mesafesindeki zaman noktalarının tüm olası çiftlerinin ortalamasının alınmasıdır .

İhtiyacım olan şeyi yapan işlevi şu şekilde tanımladım:

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

Bana öyle geliyor ki önceki cevapların hiçbiri bu otomatik / çapraz korelasyon örneğini kapsamıyor: umarım bu cevap benim gibi stokastik süreçler üzerinde çalışan biri için yararlı olabilir.


1

Talib.CORREL'i bu şekilde otokorelasyon için kullanıyorum, aynısını diğer paketlerle de yapabileceğinizi düşünüyorum:

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

Fourier dönüşümü ve evrişim teoremini kullanma

Zaman karmaşıklığı N * log (N)

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

İşte normalleştirilmiş ve tarafsız bir versiyon, aynı zamanda 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 tarafından sağlanan yöntem işe yarıyor, ancak bilgisayarımda test ettim, zaman karmaşıklığı N * N gibi görünüyor

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

1

Numpy.correlate'e bir alternatif, statsmodels.tsa.stattools.acf () ' de mevcuttur . Bu, OP tarafından açıklanan gibi sürekli olarak azalan bir otokorelasyon işlevi sağlar. Bunu uygulamak oldukça basittir:

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]

Varsayılan davranış, 40 nlag'de durmaktır, ancak bu, nlag=özel uygulamanız için seçenekle ayarlanabilir . Fonksiyonun arkasındaki istatistikler için sayfanın alt kısmında bir alıntı var .


0

Bence OP'nin sorusuna verilen gerçek yanıt, Numpy.correlate belgelerinden bu alıntıda kısaca yer almaktadır:

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

Bu, 'mod' tanımı olmadan kullanıldığında, Numpy.correlate fonksiyonunun, iki giriş argümanı için aynı vektör verildiğinde (yani - otokorelasyonu gerçekleştirmek için kullanıldığında) bir skaler döndüreceği anlamına gelir.


0

Pandalar olmadan basit bir çözüm:

import numpy as np

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

0

Pandalar veri zamanına göre istatistiksel otokorelasyonu çizin Geri dönüş serisi:

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)

Neden autocorrelation_plot()bu durumda kullanmıyorsunuz ? (bkz. stats.stackexchange.com/questions/357300/… )
Qaswed
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.