python - ols - ¿Cuál es la diferencia entre pandas ACF y statsmodel ACF?
statsmodels time series (2)
Estoy calculando la función de autocorrelación para las devoluciones de una acción. Para ello, probé dos funciones, la función de statsmodels.tsa
incorporada en Pandas y la función acf
proporcionada por statsmodels.tsa
. Esto se hace en el siguiente MWE:
import pandas as pd
from pandas_datareader import data
import matplotlib.pyplot as plt
import datetime
from dateutil.relativedelta import relativedelta
from statsmodels.tsa.stattools import acf, pacf
ticker = ''AAPL''
time_ago = datetime.datetime.today().date() - relativedelta(months = 6)
ticker_data = data.get_data_yahoo(ticker, time_ago)[''Adj Close''].pct_change().dropna()
ticker_data_len = len(ticker_data)
ticker_data_acf_1 = acf(ticker_data)[1:32]
ticker_data_acf_2 = [ticker_data.autocorr(i) for i in range(1,32)]
test_df = pd.DataFrame([ticker_data_acf_1, ticker_data_acf_2]).T
test_df.columns = [''Pandas Autocorr'', ''Statsmodels Autocorr'']
test_df.index += 1
test_df.plot(kind=''bar'')
Lo que noté fue que los valores que predijeron no eran idénticos:
¿Qué explica esta diferencia y qué valores deben usarse?
Como se sugiere en los comentarios, el problema puede reducirse, pero no resolverse por completo, proporcionando unbiased=True
a la función de statsmodels
. Usando una entrada aleatoria:
import statistics
import numpy as np
import pandas as pd
from statsmodels.tsa.stattools import acf
DATA_LEN = 100
N_TESTS = 100
N_LAGS = 32
def test(unbiased):
data = pd.Series(np.random.random(DATA_LEN))
data_acf_1 = acf(data, unbiased=unbiased, nlags=N_LAGS)
data_acf_2 = [data.autocorr(i) for i in range(N_LAGS+1)]
# return difference between results
return sum(abs(data_acf_1 - data_acf_2))
for value in (False, True):
diffs = [test(value) for _ in range(N_TESTS)]
print(value, statistics.mean(diffs))
Salida:
False 0.464562410987
True 0.0820847168593
La diferencia entre las versiones de Pandas y Statsmodels se encuentra en la resta media y la división de normalización / varianza:
-
autocorr
no hace nada más que pasar una subserie de la serie original anp.corrcoef
. Dentro de este método, la media muestral y la varianza muestral de estas subseries se utilizan para determinar el coeficiente de correlación. -
acf
, por el contrario, utiliza la media muestral de la serie global y la varianza muestral para determinar el coeficiente de correlación.
Las diferencias pueden reducirse para series de tiempo más largas, pero son bastante grandes para series cortas.
En comparación con Matlab, la función de autocorr
Pandas probablemente corresponde a hacer Matlabs xcorr
(cross-corr) con la propia serie (retrasada), en lugar de autocorr
de Matlab, que calcula la autocorrelación de la muestra (adivinando de los documentos; no puedo validar esto porque tengo No hay acceso a Matlab).
Ver este MWE para la aclaración:
import numpy as np
import pandas as pd
from statsmodels.tsa.stattools import acf
import matplotlib.pyplot as plt
plt.style.use("seaborn-colorblind")
def autocorr_by_hand(x, lag):
# Slice the relevant subseries based on the lag
y1 = x[:(len(x)-lag)]
y2 = x[lag:]
# Subtract the subseries means
sum_product = np.sum((y1-np.mean(y1))*(y2-np.mean(y2)))
# Normalize with the subseries stds
return sum_product / ((len(x) - lag) * np.std(y1) * np.std(y2))
def acf_by_hand(x, lag):
# Slice the relevant subseries based on the lag
y1 = x[:(len(x)-lag)]
y2 = x[lag:]
# Subtract the mean of the whole series x to calculate Cov
sum_product = np.sum((y1-np.mean(x))*(y2-np.mean(x)))
# Normalize with var of whole series
return sum_product / ((len(x) - lag) * np.var(x))
x = np.linspace(0,100,101)
results = {}
nlags=10
results["acf_by_hand"] = [acf_by_hand(x, lag) for lag in range(nlags)]
results["autocorr_by_hand"] = [autocorr_by_hand(x, lag) for lag in range(nlags)]
results["autocorr"] = [pd.Series(x).autocorr(lag) for lag in range(nlags)]
results["acf"] = acf(x, unbiased=True, nlags=nlags-1)
pd.DataFrame(results).plot(kind="bar", figsize=(10,5), grid=True)
plt.xlabel("lag")
plt.ylim([-1.2, 1.2])
plt.ylabel("value")
plt.show()
Statsmodels usa np.correlate
para optimizar esto, pero así es básicamente cómo funciona.