Comment puis-je utiliser numpy.correlate pour effectuer une autocorrélation?

107

J'ai besoin de faire l'auto-corrélation d'un ensemble de nombres, ce qui, d'après ce que je comprends, n'est que la corrélation de l'ensemble avec lui-même.

Je l'ai essayé en utilisant la fonction de corrélation de numpy, mais je ne crois pas au résultat, car il donne presque toujours un vecteur où le premier nombre n'est pas le plus grand, comme il se doit.

Donc, cette question est en réalité deux questions:

  1. Que fait exactement numpy.correlate?
  2. Comment puis-je l'utiliser (ou autre chose) pour faire une auto-corrélation?
Ben
la source
Voir aussi: stackoverflow.com/questions/12269834/… pour plus d'informations sur l'autocorrélation normalisée.
amcnabb

Réponses:

115

Pour répondre à votre première question, numpy.correlate(a, v, mode)exécute la convolution de aavec l'inverse de vet donne les résultats coupés par le mode spécifié. La définition de la convolution , C (t) = ∑ -∞ <i <∞ a i v t + i où -∞ <t <∞, permet des résultats de -∞ à ∞, mais vous ne pouvez évidemment pas stocker un infiniment long tableau. Il faut donc le découper, et c'est là que le mode entre en jeu. Il existe 3 modes différents: complet, identique et valide:

  • Le mode "complet" renvoie des résultats pour tous les tdeux aet vse chevauchent.
  • Le mode «même» renvoie un résultat de même longueur que le vecteur le plus court ( aou v).
  • Le mode "valide" ne renvoie les résultats que lorsque aet se vchevauchent complètement. La documentation de numpy.convolvedonne plus de détails sur les modes.

Pour votre deuxième question, je pense numpy.correlate est de vous donner l'auto - corrélation, il est juste de vous donner un peu plus aussi. L'autocorrélation est utilisée pour déterminer à quel point un signal ou une fonction est similaire à lui-même à une certaine différence de temps. À une différence de temps de 0, l'autocorrélation doit être la plus élevée car le signal est identique à lui-même, vous vous attendiez donc à ce que le premier élément du tableau de résultats d'autocorrélation soit le plus grand. Cependant, la corrélation ne commence pas à une différence de temps de 0. Elle commence à une différence de temps négative, se ferme à 0, puis devient positive. Autrement dit, vous vous attendiez:

autocorrélation (a) = ∑ -∞ <i <∞ a i v t + i où 0 <= t <∞

Mais ce que vous avez, c'est:

autocorrélation (a) = ∑ -∞ <i <∞ a i v t + i où -∞ <t <∞

Ce que vous devez faire est de prendre la dernière moitié de votre résultat de corrélation, et cela devrait être l'autocorrélation que vous recherchez. Une simple fonction python pour faire cela serait:

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

Vous aurez bien sûr besoin d'une vérification des erreurs pour vous assurer qu'il xs'agit bien d'un tableau 1-d. De plus, cette explication n'est probablement pas la plus mathématiquement rigoureuse. J'ai jeté autour des infinis parce que la définition de la convolution les utilise, mais cela ne s'applique pas nécessairement à l'autocorrélation. Donc, la partie théorique de cette explication peut être légèrement bancale, mais j'espère que les résultats pratiques seront utiles. Ces pages sur l'autocorrélation sont très utiles et peuvent vous donner un bien meilleur contexte théorique si cela ne vous dérange pas de parcourir la notation et les concepts lourds.

A. Levy
la source
6
Dans les versions actuelles de numpy, le mode «même» peut être spécifié pour obtenir exactement ce que l'A. Levy a proposé. Le corps de la fonction pourrait alors lirereturn numpy.correlate(x, x, mode='same')
David Zwicker
13
@DavidZwicker mais les résultats sont différents! np.correlate(x,x,mode='full')[len(x)//2:] != np.correlate(x,x,mode='same'). Par exemple, 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])}. La bonne est: np.correlate(x,x,mode='full')[len(x)-1:];{ >>> array([19, 13, 11, 5, 2])} voir le premier élément est le plus grand .
Développeur
19
Notez que cette réponse donne l'autocorrélation non normalisée.
amcnabb
4
Je pense que @Developer donne le découpage correct: [len(x)-1:]commence à partir du décalage 0. Parce que fullmode donne une taille de résultat 2*len(x)-1, A.Levy [result.size/2:]est le même que [len(x)-1:]. Il vaut mieux en faire un int, comme [result.size//2:].
Jason
J'ai trouvé que ça devait être un int, au moins en python 3.7
kevinkayaks
25

L'auto-corrélation existe en deux versions: statistique et convolution. Ils font tous les deux la même chose, sauf pour un petit détail: La version statistique est normalisée pour être sur l'intervalle [-1,1]. Voici un exemple de la façon dont vous faites la statistique:

def acf(x, length=20):
    return numpy.array([1]+[numpy.corrcoef(x[:-i], x[i:])[0,1]  \
        for i in range(1, length)])
Jonathf
la source
9
Vous voulez numpy.corrcoef[x:-i], x[i:])[0,1]dans la deuxième ligne car la valeur de retour de corrcoefest une matrice 2x2
luispedro
Quelle est la différence entre les autocorrélations statistiques et convolutives?
Daniel dit réintégrer Monica le
1
@DanielPendergast: La deuxième phrase répond que: Ils font tous les deux la même chose, sauf pour un petit détail: Le premier [statistique] est normalisé pour être sur l'intervalle [-1,1]
n1k31t4
21

Utilisez la numpy.corrcoeffonction au lieu de numpy.correlatepour calculer la corrélation statistique pour un décalage de t:

def autocorr(x, t=1):
    return numpy.corrcoef(numpy.array([x[:-t], x[t:]]))
Ramón J Romero y Vigil
la source
Les "coefficients de corrélation" ne font-ils pas référence à l'autocorrélation utilisée dans le traitement du signal et non à l'autocorrélation utilisée dans les statistiques? en.wikipedia.org/wiki/Autocorrelation#Signal_processing
Daniel dit de réintégrer Monica le
@DanielPendergast Je ne suis pas aussi familier avec le traitement du signal. De la documentation numpy: "Renvoyez les coefficients de corrélation produit-moment de Pearson." Est-ce la version de traitement du signal?
Ramón J Romero y Vigil
18

Je pense qu'il y a 2 choses qui ajoutent de la confusion à ce sujet:

  1. définition statistique vs traitement du signal: comme d'autres l'ont souligné, en statistique, nous normalisons l'autocorrélation en [-1,1].
  2. moyenne / variance partielle vs non partielle: lorsque les séries temporelles se décalent avec un décalage> 0, leur taille de chevauchement sera toujours <longueur d'origine. Utilisons-nous la moyenne et std de l'original (non partiel), ou calculons-nous toujours une nouvelle moyenne et std en utilisant le chevauchement en constante évolution (partiel) fait une différence. (Il y a probablement un terme formel pour cela, mais je vais utiliser "partiel" pour l'instant).

J'ai créé 5 fonctions qui calculent l'auto-corrélation d'un tableau 1d, avec des distinctions partielles et non partielles. Certains utilisent la formule des statistiques, d'autres utilisent la corrélation dans le sens du traitement du signal, ce qui peut également être fait via FFT. Mais tous les résultats sont des auto-corrélations dans la définition des statistiques , ils illustrent donc comment ils sont liés les uns aux autres. Code ci-dessous:

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

Voici la figure de sortie:

entrez la description de l'image ici

Nous ne voyons pas les 5 lignes car 3 d'entre elles se chevauchent (en violet). Les chevauchements sont tous des auto-corrélations non partielles. En effet, les calculs à partir des méthodes de traitement du signal (np.correlate , FFT) ne calculent pas une moyenne / std différente pour chaque chevauchement.

Notez également que le résultat fft, no padding, non-partial(ligne rouge) est différent, car il n'a pas rempli la série temporelle de 0 avant de faire la FFT, donc c'est une FFT circulaire. Je ne peux pas expliquer en détail pourquoi, c'est ce que j'ai appris d'ailleurs.

Jason
la source
12

Comme je viens de rencontrer le même problème, je voudrais partager quelques lignes de code avec vous. En fait, il existe actuellement plusieurs articles assez similaires sur l'autocorrélation dans stackoverflow. Si vous définissez l'autocorrélation comme a(x, L) = sum(k=0,N-L-1)((xk-xbar)*(x(k+L)-xbar))/sum(k=0,N-1)((xk-xbar)**2)[c'est la définition donnée dans la fonction a_correlate d'IDL et qu'elle est en accord avec ce que je vois dans la réponse 2 de la question # 12269834 ], alors ce qui suit semble donner les résultats corrects:

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

Comme vous le voyez, j'ai testé cela avec une courbe de péché et une distribution aléatoire uniforme, et les deux résultats ressemblent à ce que j'attendais. Notez que j'ai utilisé mode="same"au lieu de mode="full"comme les autres l'ont fait.

Maschu
la source
9

Votre question 1 a déjà été largement discutée dans plusieurs excellentes réponses ici.

J'ai pensé partager avec vous quelques lignes de code qui permettent de calculer l'autocorrélation d'un signal en se basant uniquement sur les propriétés mathématiques de l'autocorrélation. Autrement dit, l'autocorrélation peut être calculée de la manière suivante:

  1. soustraire la moyenne du signal et obtenir un signal non biaisé

  2. calculer la transformée de Fourier du signal non biaisé

  3. calculer la densité spectrale de puissance du signal, en prenant la norme carrée de chaque valeur de la transformée de Fourier du signal non biaisé

  4. calculer la transformée de Fourier inverse de la densité spectrale de puissance

  5. normaliser la transformée de Fourier inverse de la densité spectrale de puissance par la somme des carrés du signal non biaisé, et prendre seulement la moitié du vecteur résultant

Le code pour cela est le suivant:

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
la source
est-il possible qu'il y ait quelque chose de mal à cela? Je ne peux pas faire correspondre ses résultats avec d'autres fonctions d'auto-corrélation. La fonction semble similaire mais semble quelque peu écrasée.
pindakaas
@pindakaas pourriez-vous être plus précis? veuillez fournir des informations sur les écarts que vous trouvez, avec quelles fonctions.
Ruggero
Pourquoi ne pas l'utiliser p = np.abs(f)?
dylnan
@dylnan Cela donnerait les modules des composantes de f, alors que là on veut ici un vecteur contenant les modules carrés des composantes de f.
Ruggero
1
Ouais, mais avez-vous réalisé que la compréhension des listes est probablement encore plus lente.
Jason le
2

Je suis un biologiste informatique, et quand j'ai dû calculer les auto / corrélations croisées entre des couples de séries chronologiques de processus stochastiques, j'ai réalisé que cela np.correlatene faisait pas le travail dont j'avais besoin.

En effet, ce qui semble manquer, np.correlatec'est le moyennage sur tous les couples possibles de points temporels à distance 𝜏.

Voici comment j'ai défini une fonction faisant ce dont j'avais besoin:

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

Il me semble qu'aucune des réponses précédentes ne couvre cette instance d'auto / corrélation croisée: j'espère que cette réponse pourra être utile à quelqu'un travaillant sur des processus stochastiques comme moi.

Orso
la source
1

J'utilise talib.CORREL pour une autocorrélation comme celle-ci, je suppose que vous pourriez faire la même chose avec d'autres packages:

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
la source
1

Utilisation de la transformation de Fourier et du théorème de convolution

La complexité temporelle est N * log (N)

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

Voici une version normalisée et non biaisée, c'est aussi 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]

La méthode fournie par A. Levy fonctionne, mais je l'ai testée sur mon PC, sa complexité temporelle semble être N * N

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

Une alternative à numpy.correlate est disponible dans statsmodels.tsa.stattools.acf () . Cela donne une fonction d'autocorrélation décroissante en continu comme celle décrite par OP. La mise en œuvre est assez simple:

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]

Le comportement par défaut est de s'arrêter à 40 nlags, mais cela peut être ajusté avec l' nlag=option pour votre application spécifique. Il y a une citation au bas de la page pour les statistiques derrière la fonction .

pêcheur
la source
0

Je pense que la vraie réponse à la question du PO est succinctement contenue dans cet extrait de la documentation Numpy.correlate:

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

Cela implique que, lorsqu'elle est utilisée sans définition de «mode», la fonction Numpy.correlate renverra un scalaire, lorsqu'elle aura le même vecteur pour ses deux arguments d'entrée (c'est-à-dire lorsqu'elle est utilisée pour effectuer une autocorrélation).

dbanas
la source
0

Une solution simple sans pandas:

import numpy as np

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

Tracez l'autocorrélation statistique en fonction d'une série de retours 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)
Antonio Catalano
la source
Pourquoi ne pas l'utiliser autocorrelation_plot()dans ce cas? (cf. stats.stackexchange.com/questions/357300/… )
Qaswed le