Bagaimana cara menggunakan numpy.correlate untuk melakukan autokorelasi?

107

Saya perlu melakukan korelasi otomatis dari satu set angka, yang seperti yang saya pahami itu hanya korelasi dari himpunan itu sendiri.

Saya sudah mencobanya menggunakan fungsi korelasi numpy, tetapi saya tidak percaya hasilnya, karena hampir selalu memberikan vektor di mana angka pertama bukan yang terbesar, seperti yang seharusnya.

Jadi, pertanyaan ini sebenarnya adalah dua pertanyaan:

  1. Apa sebenarnya yang numpy.correlatedilakukannya?
  2. Bagaimana saya bisa menggunakannya (atau yang lainnya) untuk melakukan korelasi otomatis?
Ben
sumber
Lihat juga: stackoverflow.com/questions/12269834/… untuk informasi tentang autokorelasi yang dinormalisasi.
amcnabb

Jawaban:

115

Untuk menjawab pertanyaan pertama Anda, numpy.correlate(a, v, mode)lakukan konvolusi adengan kebalikan dari vdan memberikan hasil dipotong oleh mode yang ditentukan. The definisi lilit , C (t) = Σ -∞ <i <∞ sebuah i v t + i mana -∞ <t <∞, memungkinkan untuk hasil dari -∞ hingga ∞, tetapi Anda jelas tidak bisa menyimpan panjang tak terhingga Himpunan. Jadi itu harus dipotong, dan di situlah mode masuk Ada 3 mode berbeda: penuh, sama, & valid:

  • Mode "penuh" mengembalikan hasil untuk setiap ttempat adan vmemiliki beberapa tumpang tindih.
  • Mode "sama" mengembalikan hasil dengan panjang yang sama dengan vektor terpendek ( aatau v).
  • Mode "valid" mengembalikan hasil hanya jika adan vbenar - benar tumpang tindih satu sama lain. The dokumentasi untuk numpy.convolvememberikan detail lebih lanjut tentang mode.

Untuk pertanyaan kedua Anda, menurut saya numpy.correlate adalah memberi Anda autokorelasi, itu hanya memberi Anda sedikit lebih banyak juga. Autokorelasi digunakan untuk menemukan seberapa mirip suatu sinyal, atau fungsinya, dengan dirinya sendiri pada perbedaan waktu tertentu. Pada perbedaan waktu 0, korelasi otomatis harus menjadi yang tertinggi karena sinyalnya identik dengan dirinya sendiri, jadi Anda berharap bahwa elemen pertama dalam larik hasil autokorelasi akan menjadi yang terbesar. Namun, korelasi tidak dimulai pada perbedaan waktu 0. Ini dimulai pada perbedaan waktu negatif, mendekati 0, dan kemudian menjadi positif. Artinya, Anda mengharapkan:

autokorelasi (a) = ∑ -∞ <i <∞ a i v t + i dimana 0 <= t <∞

Tapi yang Anda dapatkan adalah:

autokorelasi (a) = ∑ -∞ <i <∞ a i v t + i dimana -∞ <t <∞

Yang perlu Anda lakukan adalah mengambil separuh terakhir dari hasil korelasi Anda, dan itu harus menjadi autokorelasi yang Anda cari. Fungsi python sederhana untuk melakukan itu adalah:

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

Anda tentu saja memerlukan pemeriksaan kesalahan untuk memastikan bahwa xitu sebenarnya adalah larik 1-d. Juga, penjelasan ini mungkin bukan yang paling teliti secara matematis. Saya telah membahas ketidakterbatasan karena definisi konvolusi menggunakannya, tetapi itu tidak selalu berlaku untuk autokorelasi. Jadi, porsi teoritis dari penjelasan ini mungkin sedikit miring, tapi semoga hasil praktisnya bermanfaat. Halaman - halaman tentang autokorelasi ini cukup membantu, dan dapat memberi Anda latar belakang teoretis yang jauh lebih baik jika Anda tidak keberatan mengarungi notasi dan konsep yang berat.

A. Retribusi
sumber
6
Dalam build numpy saat ini, mode 'sama' dapat ditentukan untuk mencapai apa yang diusulkan A. Levy. Badan fungsi kemudian dapat membacareturn numpy.correlate(x, x, mode='same')
David Zwicker
13
@DavidZwicker tetapi hasilnya berbeda! np.correlate(x,x,mode='full')[len(x)//2:] != np.correlate(x,x,mode='same'). Misalnya, 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])}. Yang benar adalah: np.correlate(x,x,mode='full')[len(x)-1:];{ >>> array([19, 13, 11, 5, 2])} lihat item pertama adalah yang terbesar .
Pengembang
19
Perhatikan bahwa jawaban ini memberikan autokorelasi yang tidak dinormalisasi.
amcnabb
4
Saya pikir @Developer memberikan pembagian yang benar: [len(x)-1:]dimulai dari 0-lag. Karena fullmode memberikan ukuran hasil 2*len(x)-1, A.Levy [result.size/2:]sama dengan [len(x)-1:]. Lebih baik membuatnya menjadi int, seperti [result.size//2:].
Jason
Saya menemukan itu pasti int, setidaknya di python 3.7
kevinkayaks
25

Korelasi otomatis hadir dalam dua versi: statistik dan konvolusi. Keduanya melakukan hal yang sama, kecuali untuk sedikit detail: Versi statistik dinormalisasi menjadi pada interval [-1,1]. Berikut adalah contoh bagaimana Anda melakukan statistik:

def acf(x, length=20):
    return numpy.array([1]+[numpy.corrcoef(x[:-i], x[i:])[0,1]  \
        for i in range(1, length)])
jonathf
sumber
9
Anda ingin numpy.corrcoef[x:-i], x[i:])[0,1]di baris kedua sebagai nilai kembalian corrcoefadalah matriks 2x2
luispedro
Apa perbedaan antara autokorelasi statistik dan konvolusional?
Daniel mengatakan Kembalikan Monica
1
@DanielPendergast: Kalimat kedua menjawab bahwa: Mereka berdua melakukan hal yang sama, kecuali untuk sedikit detail: Yang pertama [statistik] dinormalisasi menjadi pada interval [-1,1]
n1k31t4
21

Gunakan numpy.corrcoeffungsi sebagai ganti numpy.correlatemenghitung korelasi statistik untuk lag dari t:

def autocorr(x, t=1):
    return numpy.corrcoef(numpy.array([x[:-t], x[t:]]))
Ramón J Romero y Vigil
sumber
Bukankah "koefisien korelasi" mengacu pada autokorelasi yang digunakan dalam pemrosesan sinyal dan bukan autokorelasi yang digunakan dalam statistik? en.wikipedia.org/wiki/Autocorrelation#Signal_processing
Daniel berkata Reinstate Monica
@DanielPendergast Saya tidak begitu paham dengan pemrosesan sinyal. Dari dokumen numpy: "Kembalikan koefisien korelasi momen-produk Pearson.". Apakah itu versi pemrosesan sinyal?
Ramón J Romero y Vigil
18

Menurut saya ada 2 hal yang menambah kebingungan pada topik ini:

  1. statistik vs definisi pemrosesan sinyal: seperti yang ditunjukkan orang lain, dalam statistik kami menormalkan korelasi otomatis menjadi [-1,1].
  2. mean / varians parsial vs non-parsial: ketika deretan waktu bergeser dengan kelambatan> 0, ukuran tumpang tindihnya akan selalu <panjang aslinya. Apakah kita menggunakan mean dan std dari yang asli (non-parsial), atau selalu menghitung mean dan std baru menggunakan tumpang tindih yang terus berubah (parsial) membuat perbedaan. (Mungkin ada istilah formal untuk ini, tapi saya akan menggunakan "parsial" untuk saat ini).

Saya telah membuat 5 fungsi yang menghitung korelasi otomatis dari array 1d, dengan perbedaan parsial vs non-parsial. Beberapa menggunakan rumus dari statistik, beberapa menggunakan korelasi dalam pengertian pemrosesan sinyal, yang juga dapat dilakukan melalui FFT. Tetapi semua hasil merupakan korelasi otomatis dalam definisi statistik , jadi hasil tersebut menggambarkan bagaimana mereka ditautkan satu sama lain. Kode di bawah ini:

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

Berikut adalah gambar keluarannya:

masukkan deskripsi gambar di sini

Kami tidak melihat semua 5 baris karena 3 di antaranya tumpang tindih (di ungu). Semua tumpang tindih adalah korelasi otomatis non-parsial. Ini karena perhitungan dari metode pemrosesan sinyal ( np.correlate, FFT) tidak menghitung mean / std yang berbeda untuk setiap tumpang tindih.

Perhatikan juga bahwa hasil fft, no padding, non-partial(garis merah) berbeda, karena tidak mengisi deretan waktu dengan 0 sebelum melakukan FFT, jadi FFT melingkar. Saya tidak bisa menjelaskan secara detail mengapa, itulah yang saya pelajari dari tempat lain.

Jason
sumber
12

Karena saya baru saja mengalami masalah yang sama, saya ingin berbagi beberapa baris kode dengan Anda. Sebenarnya ada beberapa posting yang agak mirip tentang autokorelasi di stackoverflow sekarang. Jika Anda mendefinisikan autokorelasi sebagai a(x, L) = sum(k=0,N-L-1)((xk-xbar)*(x(k+L)-xbar))/sum(k=0,N-1)((xk-xbar)**2)[ini adalah definisi yang diberikan dalam fungsi a_correlate IDL dan itu sesuai dengan apa yang saya lihat dalam jawaban 2 dari pertanyaan # 12269834 ], maka berikut ini tampaknya memberikan hasil yang benar:

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

Seperti yang Anda lihat, saya telah menguji ini dengan kurva dosa dan distribusi acak yang seragam, dan kedua hasil terlihat seperti yang saya harapkan. Perhatikan bahwa saya menggunakan mode="same"alih-alih mode="full"seperti yang dilakukan orang lain.

maschu
sumber
9

Pertanyaan Anda 1 telah dibahas secara ekstensif dalam beberapa jawaban yang sangat baik di sini.

Saya berpikir untuk berbagi dengan Anda beberapa baris kode yang memungkinkan Anda untuk menghitung autokorelasi sinyal hanya berdasarkan pada sifat matematika dari autokorelasi. Artinya, autokorelasi dapat dihitung dengan cara berikut:

  1. kurangi mean dari sinyal dan dapatkan sinyal yang tidak bias

  2. hitung transformasi Fourier dari sinyal yang tidak bias

  3. hitung kepadatan spektral daya sinyal, dengan mengambil norma kuadrat dari setiap nilai transformasi Fourier dari sinyal yang tidak bias

  4. menghitung transformasi Fourier terbalik dari kepadatan spektral daya

  5. menormalkan transformasi Fourier terbalik dari kepadatan spektral daya dengan jumlah kuadrat dari sinyal yang tidak bias, dan hanya mengambil setengah dari vektor yang dihasilkan

Kode untuk melakukan ini adalah sebagai berikut:

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)
Ruggero
sumber
mungkinkah ada yang salah dengan ini? Saya tidak bisa mencocokkan hasilnya dengan fungsi korelasi otomatis lainnya. Fungsinya terlihat serupa tetapi tampaknya agak padat.
pindakaas
@pindakaas bisakah kamu lebih spesifik? berikan informasi tentang perbedaan apa yang Anda temukan, dengan fungsinya.
Ruggero
Mengapa tidak digunakan p = np.abs(f)?
dylnan
@dylnan Itu akan memberikan modul dari komponen f, sedangkan di sini kita ingin sebuah vektor yang berisi modul persegi dari komponen f.
Ruggero
1
Ya, tetapi apakah Anda menyadari bahwa melakukan pemahaman daftar mungkin lebih lambat.
Jason
2

Saya seorang ahli biologi komputasi, dan ketika saya harus menghitung auto / korelasi silang antara pasangan rangkaian waktu proses stokastik, saya menyadari bahwa np.correlatetidak melakukan pekerjaan yang saya butuhkan.

Memang, apa yang tampaknya hilang np.correlateadalah rata - rata dari semua kemungkinan pasangan titik waktu pada jarak 𝜏.

Berikut adalah cara saya mendefinisikan fungsi melakukan apa yang saya butuhkan:

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

Menurut saya, tidak ada jawaban sebelumnya yang mencakup contoh auto / korelasi silang ini: semoga jawaban ini berguna bagi seseorang yang mengerjakan proses stokastik seperti saya.

Orso
sumber
1

Saya menggunakan talib.CORREL untuk autokorelasi seperti ini, saya curiga Anda dapat melakukan hal yang sama dengan paket lain:

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)
litepresence
sumber
1

Menggunakan transformasi Fourier dan teorema konvolusi

Kompleksitas waktu adalah N * log (N)

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

Ini adalah versi yang dinormalisasi dan tidak bias, ini juga 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]

Metode yang disediakan oleh A. Levy berfungsi, tetapi saya mengujinya di PC saya, kerumitan waktunya tampaknya N * N

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

Alternatif untuk numpy.correlate tersedia di statsmodels.tsa.stattools.acf () . Ini menghasilkan fungsi autokorelasi yang terus menurun seperti yang dijelaskan oleh OP. Menerapkannya cukup sederhana:

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]

Perilaku defaultnya adalah berhenti di 40 nlag, tetapi ini dapat disesuaikan dengan nlag=opsi untuk aplikasi spesifik Anda. Ada kutipan di bagian bawah halaman untuk statistik di balik fungsi tersebut .

nelayan
sumber
0

Saya pikir jawaban sebenarnya untuk pertanyaan OP secara ringkas terkandung dalam kutipan dari dokumentasi Numpy.correlate ini:

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

Ini menyiratkan bahwa, ketika digunakan tanpa definisi 'mode', fungsi Numpy.correlate akan mengembalikan skalar, ketika diberi vektor yang sama untuk dua argumen inputnya (yaitu - ketika digunakan untuk melakukan autokorelasi).

dbanas
sumber
0

Solusi sederhana tanpa panda:

import numpy as np

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

Plot autokorelasi statistik yang diberi seri datatime panda:

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)
Antonio Catalano
sumber
Mengapa tidak digunakan autocorrelation_plot()dalam kasus ini? (cf. stats.stackexchange.com/questions/357300/… )
Qaswed