Wie kann ich numpy.correlate verwenden, um eine Autokorrelation durchzuführen?


106

Ich muss eine automatische Korrelation einer Menge von Zahlen durchführen, was meines Wissens nur die Korrelation der Menge mit sich selbst ist.

Ich habe es mit der Korrelationsfunktion von numpy versucht, aber ich glaube dem Ergebnis nicht, da es fast immer einen Vektor ergibt, bei dem die erste Zahl nicht die größte ist, wie es sein sollte.

Diese Frage besteht also eigentlich aus zwei Fragen:

  1. Was genau numpy.correlatemacht das?
  2. Wie kann ich es (oder etwas anderes) verwenden, um eine Autokorrelation durchzuführen?

Siehe auch: stackoverflow.com/questions/12269834/… für Informationen zur normalisierten Autokorrelation.
Amcnabb

Antworten:


113

Um Ihre erste Frage zu beantworten, numpy.correlate(a, v, mode)führen Sie die Faltung amit der Umkehrung von durch vund geben Sie die Ergebnisse an, die durch den angegebenen Modus abgeschnitten wurden. Die Definition der Faltung , C (t) = ∑ -∞ <i <∞ a i v t + i wobei -∞ <t <∞, ermöglicht Ergebnisse von -∞ bis ∞, aber Sie können offensichtlich nicht unendlich lange speichern Array. Es muss also abgeschnitten werden, und hier kommt der Modus ins Spiel. Es gibt drei verschiedene Modi: voll, gleich und gültig:

  • „full“ Modus liefert Ergebnisse für jede two beide aund vhaben eine gewisse Überlappung.
  • Der "gleiche" Modus gibt ein Ergebnis mit der gleichen Länge wie der kürzeste Vektor ( aoder v) zurück.
  • „gültig“ Modus liefert Ergebnisse nur , wenn aund vvollständig überlappen einander. In der Dokumentation zu finden Sie numpy.convolveweitere Informationen zu den Modi.

Ihre zweite Frage, ich denke , numpy.correlate wird Ihnen die Autokorrelation zu geben, wird es geben Sie nur ein wenig mehr als gut. Die Autokorrelation wird verwendet, um herauszufinden, wie ähnlich ein Signal oder eine Funktion zu einem bestimmten Zeitunterschied sich selbst ist. Bei einer Zeitdifferenz von 0 sollte die Autokorrelation am höchsten sein, da das Signal mit sich selbst identisch ist. Sie haben also erwartet, dass das erste Element im Ergebnisfeld der Autokorrelation das größte ist. Die Korrelation beginnt jedoch nicht mit einer Zeitdifferenz von 0. Sie beginnt mit einer negativen Zeitdifferenz, schließt sich zu 0 und wird dann positiv. Das heißt, Sie haben erwartet:

Autokorrelation (a) = ∑ -∞ <i <∞ a i v t + i wobei 0 <= t <∞

Aber was du hast war:

Autokorrelation (a) = ∑ -∞ <i <∞ a i v t + i wobei -∞ <t <∞

Was Sie tun müssen, ist die letzte Hälfte Ihres Korrelationsergebnisses zu nehmen, und das sollte die Autokorrelation sein, nach der Sie suchen. Eine einfache Python-Funktion dazu wäre:

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

Sie müssen natürlich eine Fehlerprüfung durchführen, um sicherzustellen, dass xes sich tatsächlich um ein 1-D-Array handelt. Auch diese Erklärung ist wahrscheinlich nicht die mathematisch strengste. Ich habe Unendlichkeiten herumgeworfen, weil die Definition der Faltung sie verwendet, aber das gilt nicht unbedingt für die Autokorrelation. Der theoretische Teil dieser Erklärung mag etwas wackelig sein, aber hoffentlich sind die praktischen Ergebnisse hilfreich. Diese Seiten zur Autokorrelation sind ziemlich hilfreich und können Ihnen einen viel besseren theoretischen Hintergrund bieten, wenn es Ihnen nichts ausmacht, durch die Notation und die umfangreichen Konzepte zu waten.


6
In aktuellen Numpy-Builds kann der Modus 'same' angegeben werden, um genau das zu erreichen, was die A. Levy vorgeschlagen hat. Der Körper der Funktion konnte dann lesenreturn numpy.correlate(x, x, mode='same')
David Zwicker

13
@ DavidZwicker aber die Ergebnisse sind anders! np.correlate(x,x,mode='full')[len(x)//2:] != np.correlate(x,x,mode='same'). Zum Beispiel 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])}. Das richtige ist: np.correlate(x,x,mode='full')[len(x)-1:];{ >>> array([19, 13, 11, 5, 2])} siehe, das erste Element ist das größte .
Entwickler

19
Beachten Sie, dass diese Antwort die nicht normalisierte Autokorrelation ergibt.
Amcnabb

4
Ich denke, @Developer gibt das richtige Slicing: [len(x)-1:]beginnt mit der 0-Verzögerung. Da der fullModus eine Ergebnisgröße ergibt 2*len(x)-1, ist A.Levy's [result.size/2:]dieselbe wie [len(x)-1:]. Es ist jedoch besser, es zu einem Int zu machen [result.size//2:].
Jason

Ich fand, dass es ein int sein muss, zumindest in Python 3.7
Kevinkayaks

25

Die Autokorrelation gibt es in zwei Versionen: statistisch und Faltung. Beide machen dasselbe, bis auf ein kleines Detail: Die statistische Version ist so normalisiert, dass sie im Intervall [-1,1] liegt. Hier ist ein Beispiel, wie Sie die statistische erstellen:

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

9
Sie möchten numpy.corrcoef[x:-i], x[i:])[0,1]in der zweiten Zeile als Rückgabewert corrcoefeine 2x2-Matrix
luispedro

Was ist der Unterschied zwischen der statistischen und der Faltungsautokorrelation?
Daniel sagt Reinstate Monica

1
@ DanielPendergast: Der zweite Satz antwortet: Beide machen dasselbe, bis auf ein kleines Detail: Ersteres [statistisch] wird normalisiert, um im Intervall [-1,1]
n1k31t4

21

Verwenden Sie numpy.corrcoefstattdessen die Funktion numpy.correlate, um die statistische Korrelation für eine Verzögerung von t zu berechnen:

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

Beziehen sich "Korrelationskoeffizienten" nicht auf die bei der Signalverarbeitung verwendete Autokorrelation und nicht auf die in der Statistik verwendete Autokorrelation? en.wikipedia.org/wiki/Autocorrelation#Signal_processing
Daniel sagt Reinstate Monica

@ DanielPendergast Ich bin nicht so vertraut mit der Signalverarbeitung. Aus den numpy-Dokumenten: "Pearson-Produkt-Moment-Korrelationskoeffizienten zurückgeben." Ist das die Signalverarbeitungsversion?
Ramón J Romero y Vigil

18

Ich denke, es gibt zwei Dinge, die dieses Thema verwirren:

  1. statistische vs. Signalverarbeitungsdefinition: Wie andere betont haben, normalisieren wir in der Statistik die Autokorrelation in [-1,1].
  2. partieller oder nicht partieller Mittelwert / Varianz: Wenn sich die Zeitreihen mit einer Verzögerung> 0 verschieben, ist ihre Überlappungsgröße immer <ursprüngliche Länge. Verwenden wir den Mittelwert und den Standard des Originals (nicht partiell) oder berechnen wir immer einen neuen Mittelwert und den Standard unter Verwendung der sich ständig ändernden Überlappung (teilweise), was einen Unterschied macht. (Es gibt wahrscheinlich einen formalen Begriff dafür, aber ich werde vorerst "teilweise" verwenden).

Ich habe 5 Funktionen erstellt, die die Autokorrelation eines 1d-Arrays mit partiellen und nicht partiellen Unterscheidungen berechnen. Einige verwenden Formeln aus Statistiken, andere Korrelate im Sinne der Signalverarbeitung, was auch über FFT erfolgen kann. Alle Ergebnisse sind jedoch Autokorrelationen in der Statistikdefinition , sodass sie veranschaulichen, wie sie miteinander verknüpft sind. Code unten:

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

Hier ist die Ausgabezahl:

Geben Sie hier die Bildbeschreibung ein

Wir sehen nicht alle 5 Linien, da sich 3 davon überlappen (lila). Die Überlappungen sind alle nicht partielle Autokorrelationen. Dies liegt daran, dass Berechnungen aus den Signalverarbeitungsverfahren ( np.correlateFFT) nicht für jede Überlappung einen anderen Mittelwert / Standard berechnen.

Beachten Sie auch, dass das fft, no padding, non-partialErgebnis (rote Linie) anders ist, da die Zeitreihen vor der FFT nicht mit 0s aufgefüllt wurden, sodass es sich um eine kreisförmige FFT handelt. Ich kann nicht im Detail erklären, warum, das habe ich von anderswo gelernt.


12

Da ich gerade auf dasselbe Problem gestoßen bin, möchte ich ein paar Codezeilen mit Ihnen teilen. Tatsächlich gibt es mittlerweile mehrere ziemlich ähnliche Beiträge zur Autokorrelation im Stapelüberlauf. Wenn Sie die Autokorrelation als a(x, L) = sum(k=0,N-L-1)((xk-xbar)*(x(k+L)-xbar))/sum(k=0,N-1)((xk-xbar)**2)[dies ist die Definition in der a_correlate-Funktion von IDL und sie stimmt mit dem überein, was ich in Antwort 2 von Frage Nr. 12269834 sehe ] definieren, scheint das Folgende die richtigen Ergebnisse zu liefern:

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

Wie Sie sehen, habe ich dies mit einer Sinuskurve und einer gleichmäßigen Zufallsverteilung getestet, und beide Ergebnisse sehen so aus, als würde ich sie erwarten. Beachten Sie, dass ich mode="same"statt mode="full"wie die anderen verwendet habe.


9

Ihre Frage 1 wurde hier bereits ausführlich in mehreren ausgezeichneten Antworten erörtert.

Ich wollte Ihnen einige Codezeilen mitteilen, mit denen Sie die Autokorrelation eines Signals nur anhand der mathematischen Eigenschaften der Autokorrelation berechnen können. Das heißt, die Autokorrelation kann auf folgende Weise berechnet werden:

  1. subtrahieren Sie den Mittelwert vom Signal und erhalten Sie ein unverzerrtes Signal

  2. Berechnen Sie die Fourier-Transformation des unverzerrten Signals

  3. Berechnen Sie die spektrale Leistungsdichte des Signals, indem Sie die Quadratnorm jedes Werts der Fourier-Transformation des unverzerrten Signals nehmen

  4. Berechnen Sie die inverse Fourier-Transformation der spektralen Leistungsdichte

  5. Normalisieren Sie die inverse Fourier-Transformation der spektralen Leistungsdichte durch die Summe der Quadrate des unverzerrten Signals und nehmen Sie nur die Hälfte des resultierenden Vektors

Der Code dafür lautet wie folgt:

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)

ist es möglich, dass damit etwas nicht stimmt? Ich kann die Ergebnisse nicht mit anderen Autokorrelationsfunktionen abgleichen. Die Funktion sieht ähnlich aus, wirkt aber etwas gequetscht.
Pindakaas

@pindakaas könnten Sie genauer sein? Bitte geben Sie an, welche Diskrepanzen Sie bei welchen Funktionen feststellen.
Ruggero

Warum nicht verwenden p = np.abs(f)?
Dylnan

@dylnan Das würde die Module der Komponenten von f geben, während wir hier einen Vektor wollen, der die quadratischen Module der Komponenten von f enthält.
Ruggero

1
Ja, aber haben Sie erkannt, dass das Listenverständnis wahrscheinlich noch langsamer ist?
Jason

2

Ich bin ein Computerbiologe, und als ich die Auto- / Kreuzkorrelationen zwischen Paaren von Zeitreihen stochastischer Prozesse berechnen musste, stellte ich fest, dass dies np.correlatenicht die Arbeit war, die ich brauchte.

In der Tat scheint np.correlatedie Mittelung über alle möglichen Paare von Zeitpunkten in der Entfernung 𝜏 zu fehlen .

So habe ich eine Funktion definiert, die das tut, was ich brauchte:

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

Es scheint mir, dass keine der vorherigen Antworten diesen Fall der Auto- / Kreuzkorrelation abdeckt: Ich hoffe, diese Antwort kann für jemanden nützlich sein, der an stochastischen Prozessen wie mir arbeitet.


1

Ich verwende talib.CORREL für eine solche Autokorrelation. Ich vermute, Sie könnten dasselbe mit anderen Paketen tun:

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

Verwendung der Fourier-Transformation und des Faltungssatzes

Die Zeitkomplexität ist N * log (N)

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

Hier ist eine normalisierte und unvoreingenommene Version, es ist auch 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]

Die von A. Levy bereitgestellte Methode funktioniert, aber ich habe sie auf meinem PC getestet. Die zeitliche Komplexität scheint N * N zu sein

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

1

Eine Alternative zu numpy.correlate finden Sie in statsmodels.tsa.stattools.acf () . Dies ergibt eine kontinuierlich abnehmende Autokorrelationsfunktion wie die von OP beschriebene. Die Implementierung ist ziemlich einfach:

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]

Das Standardverhalten ist das Stoppen bei 40 Verzögerungen. Dies kann jedoch mit der nlag=Option für Ihre spezifische Anwendung angepasst werden . Am Ende der Seite befindet sich ein Zitat für die Statistiken hinter der Funktion .


0

Ich denke, die eigentliche Antwort auf die Frage des OP ist in diesem Auszug aus der Numpy.correlate-Dokumentation kurz und bündig enthalten:

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

Dies impliziert, dass die Numpy.correlate-Funktion bei Verwendung ohne 'mode'-Definition einen Skalar zurückgibt, wenn für ihre beiden Eingabeargumente derselbe Vektor angegeben wird (dh - wenn sie zur Durchführung der Autokorrelation verwendet wird).


0

Eine einfache Lösung ohne Pandas:

import numpy as np

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

0

Zeichnen Sie die statistische Autokorrelation bei einer Pandas-Datenzeit. Serie von Rückgaben:

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)

Warum nicht autocorrelation_plot()in diesem Fall verwenden? (vgl. stats.stackexchange.com/questions/357300/… )
Qaswed
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.