Creo que hay 2 cosas que agregan confusión a este tema:
- definición de procesamiento de señal vs estadística: como otros han señalado, en estadística normalizamos la autocorrelación en [-1,1].
- media / varianza parcial versus no parcial: cuando la serie temporal cambia con un retraso> 0, su tamaño de superposición siempre será <longitud original. ¿Usamos la media y std del original (no parcial), o siempre calculamos una nueva media y std usando la superposición en constante cambio (parcial) que marca la diferencia? (Probablemente haya un término formal para esto, pero por ahora usaré "parcial").
He creado 5 funciones que calculan la autocorrelación de una matriz 1d, con distinciones parciales y no parciales. Algunos usan fórmulas de estadísticas, algunos usan correlación en el sentido de procesamiento de señales, lo que también se puede hacer mediante FFT. Pero todos los resultados son autocorrelaciones en la definición de estadísticas , por lo que ilustran cómo están vinculados entre sí. Código a continuación:
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()
Aquí está la figura de salida:
No vemos las 5 líneas porque 3 de ellas se superponen (en la púrpura). Las superposiciones son todas autocorrelaciones no parciales. Esto se debe a que los cálculos de los métodos de procesamiento de señales ( np.correlate
, FFT) no calculan una media / estándar diferente para cada superposición.
También tenga en cuenta que el resultado fft, no padding, non-partial
(línea roja) es diferente, porque no rellenó la serie temporal con 0 antes de hacer FFT, por lo que es circular FFT. No puedo explicar en detalle por qué, eso es lo que aprendí en otros lugares.