web-dev-qa-db-fra.com

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

Je dois faire l'auto-corrélation d'un ensemble de nombres, qui, si j'ai bien compris, 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 est pas le plus grand, comme il se doit.

Donc, cette question est vraiment deux questions:

  1. Que fait numpy.correlate exactement?
  2. Comment puis-je l'utiliser (ou autre chose) pour effectuer l'auto-corrélation?
81
Ben

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

  • Le mode "complet" renvoie les résultats pour chaque ta et v ont des chevauchements. 
  • "same" mode renvoie un résultat de même longueur que le vecteur le plus court (a ou v). 
  • Le mode "valide" ne renvoie les résultats que lorsque a et v se chevauchent complètement. La documentation for numpy.convolve donne plus de détails sur les modes.

Pour votre deuxième question, je pense que numpy.correlateis vous donne l'autocorrélation, c'est juste un peu plus. L'autocorrélation permet de déterminer la similarité d'un signal, ou d'une fonction, avec lui-même à une certaine différence de temps. À une différence de temps de 0, l'auto-corrélation doit être la plus grande, 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. C'est-à-dire que vous attendiez: 

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

Mais ce que tu as eu c'est: 

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

Ce que vous devez faire, c'est 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 à faire serait:

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

Bien entendu, vous aurez besoin d’une vérification d’erreur pour vous assurer que x est en réalité un tableau 1-d. En outre, cette explication n’est probablement pas la plus rigoureuse mathématiquement. J'ai jeté des bases infinies parce que la définition de convolution les utilise, mais cela ne s'applique pas nécessairement à l'autocorrélation. Donc, la partie théorique de cette explication peut être un peu confuse, mais nous espérons que les résultats pratiques seront utiles. Cespages sur l'auto-corrélation sont très utiles et peuvent vous donner une base théorique bien meilleure si cela ne vous dérange pas de parcourir la notation et les concepts lourds.

93
A. Levy

L'auto-corrélation est disponible en deux versions: statistique et convolution. Ils font tous les deux la même chose, à l'exception d'un petit détail: la version statistique est normalisée 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)])
15
jonathf

Utilisez la fonction numpy.corrcoef au lieu de numpy.correlate pour 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:]]))
15

Comme je viens de rencontrer le même problème, je voudrais partager quelques lignes de code avec vous. En fait, il existe plusieurs publications assez similaires sur la corrélation automatique dans stackoverflow. Si vous définissez l'autocorrélation comme étant 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 si elle correspond à ce que je vois dans la réponse 2 de la question # 12269834 ], les éléments suivants semblent 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 à ceux auxquels je m'attendrais. Notez que j'ai utilisé mode="same" au lieu de mode="full" comme les autres. 

10
maschu

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

J'ai pensé partager avec vous quelques lignes de code vous permettant de calculer l'autocorrélation d'un signal en vous 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 ne prendre que la moitié du vecteur résultant

Le code à faire 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)
8
Ruggero

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

  1. statistiques v. définition du traitement du signal: comme d'autres l'ont souligné, en statistique, nous normalisons l'autocorrélation en [-1,1]. 
  2. v. partiel moyenne/variance non partielle: lorsque la série temporelle se décale avec un décalage> 0, leur taille de chevauchement sera toujours <longueur d'origine. Utilisons-nous les valeurs moyenne et standard de l'original (non partiel) ou calculons-nous toujours une nouvelle moyenne et standard en utilisant le chevauchement en constante évolution (partiel) qui fait la 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 v. Partiels. distinctions non partielles. Certains utilisent des formules issues de statistiques, d’autres utilisent corréler dans le sens du traitement du signal, ce qui peut également être effectué via FFT. Mais tous les résultats sont des auto-corrélations dans la définition de statistics, 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 le chiffre de sortie:

 enter image description here

Nous ne voyons pas les 5 lignes car 3 d'entre elles se chevauchent (en violet). Les chevauchements sont tous des corrélations automatiques non partielles. En effet, les calculs issus 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 de fft, no padding, non-partial (ligne rouge) est différent, car il n'a pas complété les séries de temps avec des 0 avant d'effectuer la FFT, il est donc circulaire FFT. Je ne peux pas expliquer en détail pourquoi, c'est ce que j'ai appris ailleurs.

7
Jason

J'utilise talib.CORREL pour l'autocorrélation de cette façon, 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)
1
litepresence

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

La complexité du temps 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 de N * N

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

Une solution simple sans pandas:

import numpy as np

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

Tracez la corrélation statistique en fonction de la date et de l'heure d'un pandas.

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)
0
Antonio Catalano

Une alternative à numpy.correlate est disponible dans statsmodels.tsa.stattools.acf () . Ceci donne une fonction d'autocorrélation décroissante de façon constante 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 consiste à arrêter à 40 nlags, mais cela peut être ajusté avec l'option nlag= pour votre application spécifique. Il y a une citation au bas de la page pour les statistiques derrière la fonction .

0
fisherp

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

En effet, ce qui semble manquer à np.correlate, c’est le calcul de la moyennesur tous les couples possibles de points de tempsà 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 cet exemple de corrélation automatique/croisée: espérons que cette réponse sera utile à quelqu'un qui travaille sur des processus stochastiques comme moi.

0
Orso

Je pense que la véritable réponse à la question du PO figure succinctement dans cet extrait de la documentation de 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 n'est utilisée avec aucune définition de «mode», la fonction Numpy.correlate renverra un scalaire si le même vecteur lui est attribué pour ses deux arguments d'entrée (c'est-à-dire lorsqu'elle est utilisée pour effectuer une autocorrélation).

0
dbanas