Come posso utilizzare numpy.correlate per eseguire l'autocorrelazione?


107

Devo fare l'auto-correlazione di un insieme di numeri, che a quanto ho capito è solo la correlazione dell'insieme con se stesso.

L'ho provato utilizzando la funzione correlata di numpy, ma non credo al risultato, poiché quasi sempre fornisce un vettore in cui il primo numero non è il più grande, come dovrebbe essere.

Quindi, questa domanda è davvero due domande:

  1. Cosa sta numpy.correlatefacendo esattamente ?
  2. Come posso usarlo (o qualcos'altro) per fare l'auto-correlazione?

Vedi anche: stackoverflow.com/questions/12269834/… per informazioni sull'autocorrelazione normalizzata.
amcnabb

Risposte:


115

Per rispondere alla tua prima domanda, numpy.correlate(a, v, mode)sta eseguendo la convoluzione di acon il contrario di ve dando i risultati tagliati dalla modalità specificata. La definizione di convoluzione , C (t) = ∑ -∞ <i <∞ a i v t + i dove -∞ <t <∞, consente risultati da -∞ a ∞, ma ovviamente non puoi memorizzare un infinitamente lungo Vettore. Quindi deve essere ritagliato, ed è qui che entra in gioco la modalità. Ci sono 3 diverse modalità: completa, uguale e valida:

  • la modalità "completa" restituisce risultati per ogni tdove sia aev hanno qualche sovrapposizione.
  • la modalità "stessa" restituisce un risultato con la stessa lunghezza del vettore più breve ( aov ).
  • La modalità "valido" restituisce risultati solo quando ae si vsovrappongono completamente l'uno all'altro. La documentazione per numpy.convolvefornisce maggiori dettagli sulle modalità.

Per la tua seconda domanda, credo che lo numpy.correlate sia che ti dando l'autocorrelazione, ti sta solo dando anche un po 'di più. L'autocorrelazione viene utilizzata per scoprire quanto un segnale, o una funzione, è simile a se stesso in una certa differenza di tempo. Con una differenza di tempo pari a 0, l'auto-correlazione dovrebbe essere la più alta perché il segnale è identico a se stesso, quindi ti aspettavi che il primo elemento nell'array dei risultati dell'autocorrelazione sarebbe stato il più grande. Tuttavia, la correlazione non inizia con una differenza di orario pari a 0. Inizia con una differenza di orario negativa, si chiude a 0 e quindi diventa positiva. Cioè, ti aspettavi:

autocorrelazione (a) = ∑ -∞ <i <∞ a i v t + i dove 0 <= t <∞

Ma quello che hai ottenuto è stato:

autocorrelazione (a) = ∑ -∞ <i <∞ a i v t + i dove -∞ <t <∞

Quello che devi fare è prendere l'ultima metà del risultato della correlazione e quella dovrebbe essere l'autocorrelazione che stai cercando. Una semplice funzione Python per farlo sarebbe:

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

Ovviamente avrai bisogno di un controllo degli errori per assicurarti che xsia effettivamente un array 1-d. Inoltre, questa spiegazione probabilmente non è la più matematicamente rigorosa. Ho gettato gli infiniti perché la definizione di convoluzione li usa, ma ciò non si applica necessariamente all'autocorrelazione. Quindi, la parte teorica di questa spiegazione potrebbe essere leggermente instabile, ma si spera che i risultati pratici siano utili. Queste pagine sull'autocorrelazione sono molto utili e possono darti un background teorico molto migliore se non ti dispiace guadare la notazione ei concetti pesanti.


6
Nelle build attuali di numpy, è possibile specificare la modalità "uguale" per ottenere esattamente ciò che proponeva A. Levy. Il corpo della funzione potrebbe quindi leggerereturn numpy.correlate(x, x, mode='same')
David Zwicker

13
@DavidZwicker ma i risultati sono diversi! np.correlate(x,x,mode='full')[len(x)//2:] != np.correlate(x,x,mode='same'). Ad esempio, 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])}. Quello corretto è: np.correlate(x,x,mode='full')[len(x)-1:];{ >>> array([19, 13, 11, 5, 2])} vedi il primo elemento è il più grande .
Sviluppatore

19
Nota che questa risposta fornisce l'autocorrelazione non normalizzata.
amcnabb

4
Penso che @Developer dia lo slicing corretto: [len(x)-1:]inizia dallo 0-lag. Poiché la fullmodalità fornisce una dimensione del risultato 2*len(x)-1, quella di A.Levy [result.size/2:]è la stessa di [len(x)-1:]. È meglio renderlo un int però, tipo [result.size//2:].
Jason

Ho scoperto che deve essere un int, almeno in Python 3.7
kevinkayaks

25

L'auto-correlazione è disponibile in due versioni: statistica e convoluzione. Entrambi fanno lo stesso, eccetto per un piccolo dettaglio: la versione statistica è normalizzata per essere sull'intervallo [-1,1]. Ecco un esempio di come fai quello statistico:

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

9
Vuoi numpy.corrcoef[x:-i], x[i:])[0,1]nella seconda riga come il valore di ritorno di corrcoefè una matrice 2x2
luispedro

Qual è la differenza tra le autocorrelazioni statistiche e convoluzionali?
Daniel dice Reinstate Monica

1
@DanielPendergast: La seconda frase risponde che: Entrambi fanno lo stesso, tranne per un piccolo dettaglio: Il primo [statistico] è normalizzato per essere sull'intervallo [-1,1]
n1k31t4

21

Utilizzare la numpy.corrcoeffunzione invece di numpy.correlateper calcolare la correlazione statistica per un ritardo di t:

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

I "coefficienti di correlazione" non si riferiscono all'autocorrelazione utilizzata nell'elaborazione del segnale e non all'autocorrelazione utilizzata nella statistica? en.wikipedia.org/wiki/Autocorrelation#Signal_processing
Daniel dice Reinstate Monica

@DanielPendergast Non ho familiarità con l'elaborazione del segnale. Dai documenti numpy: "Return Pearson product-moment correlation coefficients.". Questa è la versione di elaborazione del segnale?
Ramón J Romero y Vigil

18

Penso che ci siano 2 cose che aggiungono confusione a questo argomento:

  1. definizione statistica vs elaborazione del segnale: come altri hanno sottolineato, in statistica normalizziamo l'auto-correlazione in [-1,1].
  2. media / varianza parziale vs non parziale: quando la serie temporale si sposta con un ritardo> 0, la loro dimensione di sovrapposizione sarà sempre <lunghezza originale. Usiamo la media e lo std dell'originale (non parziale), o calcoliamo sempre una nuova media e std usando la sovrapposizione in continua evoluzione (parziale) fa la differenza. (C'è probabilmente un termine formale per questo, ma userò "parziale" per ora).

Ho creato 5 funzioni che calcolano l'auto-correlazione di un array 1d, con distinzioni parziali e non parziali. Alcuni usano formule tratte dalle statistiche, altri sono correlati nel senso dell'elaborazione del segnale, che può essere eseguita anche tramite FFT. Ma tutti i risultati sono auto-correlazioni nella definizione delle statistiche , quindi illustrano come sono collegati tra loro. Codice di seguito:

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

Ecco la figura di output:

inserisci qui la descrizione dell'immagine

Non vediamo tutte e 5 le linee perché 3 di esse si sovrappongono (in viola). Le sovrapposizioni sono tutte autocorrelazioni non parziali. Questo perché i calcoli dai metodi di elaborazione del segnale ( np.correlate, FFT) non calcolano una media / std diversa per ogni sovrapposizione.

Si noti inoltre che il risultato fft, no padding, non-partial(linea rossa) è diverso, perché non riempiva la serie temporale con 0 prima di eseguire FFT, quindi è FFT circolare. Non posso spiegare in dettaglio perché, è quello che ho imparato da altrove.


12

Dato che mi sono imbattuto nello stesso problema, vorrei condividere con te alcune righe di codice. In effetti ci sono molti post piuttosto simili sull'autocorrelazione in stackoverflow ormai. Se definisci l'autocorrelazione come a(x, L) = sum(k=0,N-L-1)((xk-xbar)*(x(k+L)-xbar))/sum(k=0,N-1)((xk-xbar)**2)[questa è la definizione data nella funzione a_correlate di IDL e concorda con ciò che vedo nella risposta 2 della domanda # 12269834 ], allora quanto segue sembra dare i risultati corretti:

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

Come vedi, l'ho testato con una curva del peccato e una distribuzione casuale uniforme, ed entrambi i risultati sembrano come me li aspetterei. Nota che ho usato al mode="same"posto degli mode="full"altri.


9

La tua domanda 1 è già stata ampiamente discussa in diverse eccellenti risposte qui.

Ho pensato di condividere con voi alcune righe di codice che permettono di calcolare l'autocorrelazione di un segnale basandosi solo sulle proprietà matematiche dell'autocorrelazione. Cioè, l'autocorrelazione può essere calcolata nel modo seguente:

  1. sottrarre la media dal segnale e ottenere un segnale corretto

  2. calcolare la trasformata di Fourier del segnale corretto

  3. calcolare la densità spettrale di potenza del segnale, prendendo la norma quadrata di ogni valore della trasformata di Fourier del segnale non corretto

  4. calcolare la trasformata inversa di Fourier della densità spettrale di potenza

  5. normalizzare la trasformata di Fourier inversa della densità spettrale di potenza dalla somma dei quadrati del segnale non corretto e prendere solo la metà del vettore risultante

Il codice per farlo è il seguente:

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)

è possibile che ci sia qualcosa di sbagliato in questo? Non riesco a far corrispondere i suoi risultati con altre funzioni di correlazione automatica. La funzione sembra simile ma sembra un po 'schiacciata.
pindakaas,

@pindakaas potresti essere più specifico? si prega di fornire informazioni su quali sono le discrepanze riscontrate, con quali funzioni.
Ruggero

Perché non usare p = np.abs(f)?
dylnan

@dylnan Ciò darebbe i moduli dei componenti di f, mentre qui vogliamo un vettore contenente i moduli quadrati dei componenti di f.
Ruggero

1
Sì, ma ti sei reso conto che la comprensione della lista è probabilmente ancora più lenta.
Jason

2

Sono un biologo computazionale e quando ho dovuto calcolare le correlazioni auto / incrociate tra coppie di serie temporali di processi stocastici ho capito che np.correlatenon stava facendo il lavoro di cui avevo bisogno.

Quello che sembra infatti mancare np.correlateè la media di tutte le possibili coppie di punti temporali a distanza 𝜏.

Ecco come ho definito una funzione che fa ciò di cui avevo bisogno:

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

Mi sembra che nessuna delle risposte precedenti copra questo esempio di correlazione automatica / incrociata: spero che questa risposta possa essere utile a qualcuno che lavora su processi stocastici come me.


1

Uso talib.CORREL per l'autocorrelazione in questo modo, sospetto che potresti fare lo stesso con altri pacchetti:

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

Utilizzo della trasformazione di Fourier e del teorema di convoluzione

La complessità temporale è N * log (N)

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

Ecco una versione normalizzata e imparziale, è anche 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]

Il metodo fornito da A. Levy funziona, ma l'ho testato sul mio PC, la sua complessità temporale sembra essere N * N

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

1

Un'alternativa a numpy.correlate è disponibile in statsmodels.tsa.stattools.acf () . Ciò produce una funzione di autocorrelazione in continua diminuzione come quella descritta da OP. L'implementazione è abbastanza semplice:

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]

Il comportamento predefinito è fermarsi a 40 nlag, ma questo può essere regolato con l' nlag=opzione per la tua applicazione specifica. C'è una citazione in fondo alla pagina per le statistiche dietro la funzione .


0

Penso che la vera risposta alla domanda dell'OP sia succintamente contenuta in questo estratto dalla documentazione di Numpy.correlate:

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

Ciò implica che, se usata senza una definizione di "modalità", la funzione Numpy.correlate restituirà uno scalare, quando viene fornito lo stesso vettore per i suoi due argomenti di input (cioè, quando viene utilizzata per eseguire l'autocorrelazione).


0

Una soluzione semplice senza panda:

import numpy as np

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

0

Traccia l'autocorrelazione statistica data una serie di rendimenti del tempo di dati di un 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)

Perché non utilizzare autocorrelation_plot()in questo caso? (cfr stats.stackexchange.com/questions/357300/… )
Qaswed,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.