comme le titre l'indique, où est passée l'option de fonction de roulement dans la commande ols de Pandas dans les modèles de statistiques? Je n'arrive pas à le trouver . Pandas me dit que Doom est en chantier:
FutureWarning: The pandas.stats.ols module is deprecated and will be removed in a future version. We refer to external packages like statsmodels, see some examples here: http://statsmodels.sourceforge.net/stable/regression.html
model = pd.ols(y=series_1, x=mmmm, window=50)
en fait, si vous faites quelque chose comme:
import statsmodels.api as sm
model = sm.OLS(series_1, mmmm, window=50).fit()
print(model.summary())
vous obtenez des résultats (la fenêtre ne gêne pas l'exécution du code) mais vous obtenez uniquement les paramètres de la régression exécutée sur l'ensemble de la période, et non la série de paramètres de chacune des périodes glissantes sur lesquelles elle devrait être utilisée.
J'ai créé un module ols
conçu pour imiter les obsolètes MovingOLS
des pandas; c'est ici .
Il comporte trois classes principales:
OLS
: régression des moindres carrés ordinaires statique (à fenêtre unique). Les sorties sont des tableaux NumPyRollingOLS
: régression par les moindres carrés ordinaires défilant sur plusieurs fenêtres. Les résultats sont des tableaux NumPy de dimension supérieure.PandasRollingOLS
: encapsule les résultats de RollingOLS
dans les pandas Series et DataFrames. Conçu pour imiter le look du module de pandas obsolètes.Notez que le module fait partie d'un package (que je suis en train de télécharger sur PyPi) et qu'il nécessite une importation inter-packages.
Les deux premières classes ci-dessus sont entièrement implémentées dans NumPy et utilisent principalement l'algèbre matricielle. RollingOLS
profite également beaucoup de la radiodiffusion. Les attributs imitent en grande partie les statistiques de modèle OLS RegressionResultsWrapper
.
Un exemple:
# Pull some data from fred.stlouisfed.org
from pandas_datareader.data import DataReader
syms = {'TWEXBMTH' : 'usd',
'T10Y2YM' : 'term_spread',
'PCOPPUSDM' : 'copper'
}
data = (DataReader(syms.keys(), 'fred', start='2000-01-01')
.pct_change()
.dropna())
data = data.rename(columns=syms)
print(data.head())
# usd term_spread copper
# DATE
# 2000-02-01 0.01260 -1.40909 -0.01997
# 2000-03-01 -0.00012 2.00000 -0.03720
# 2000-04-01 0.00564 0.51852 -0.03328
# 2000-05-01 0.02204 -0.09756 0.06135
# 2000-06-01 -0.01012 0.02703 -0.01850
# Rolling regressions
from pyfinance.ols import PandasRollingOLS
y = data.usd
x = data.drop('usd', axis=1)
window = 12 # months
model = PandasRollingOLS(y=y, x=x, window=window)
print(model.beta.head()) # Coefficients excluding the intercept
# term_spread copper
# DATE
# 2001-01-01 0.00010 0.05568
# 2001-02-01 0.00047 0.06271
# 2001-03-01 0.00147 0.03576
# 2001-04-01 0.00161 0.02956
# 2001-05-01 0.00158 -0.04497
print(model.fstat.head())
# DATE
# 2001-01-01 0.28121
# 2001-02-01 0.42602
# 2001-03-01 0.38802
# 2001-04-01 0.39230
# 2001-05-01 0.41706
# Freq: MS, Name: fstat, dtype: float64
print(model.rsq.head()) # R-squared
# DATE
# 2001-01-01 0.05882
# 2001-02-01 0.08648
# 2001-03-01 0.07938
# 2001-04-01 0.08019
# 2001-05-01 0.08482
# Freq: MS, Name: rsq, dtype: float64
Bêta roulante avec sklearn
import pandas as pd
from sklearn import linear_model
def rolling_beta(X, y, idx, window=255):
assert len(X)==len(y)
out_dates = []
out_beta = []
model_ols = linear_model.LinearRegression()
for iStart in range(0, len(X)-window):
iEnd = iStart+window
model_ols.fit(X[iStart:iEnd], y[iStart:iEnd])
#store output
out_dates.append(idx[iEnd])
out_beta.append(model_ols.coef_[0][0])
return pd.DataFrame({'beta':out_beta}, index=out_dates)
df_beta = rolling_beta(df_rtn_stocks['NDX'].values.reshape(-1, 1), df_rtn_stocks['CRM'].values.reshape(-1, 1), df_rtn_stocks.index.values, 255)
Ajouter pour compléter une solution plus rapide numpy
- qui limite les calculs aux coefficients de régression et à l'estimation finale
Fonction de régression glissante Numpy
import numpy as np
def rolling_regression(y, x, window=60):
"""
y and x must be pandas.Series
"""
# === Clean-up ============================================================
x = x.dropna()
y = y.dropna()
# === Trim acc to shortest ================================================
if x.index.size > y.index.size:
x = x[y.index]
else:
y = y[x.index]
# === Verify enough space =================================================
if x.index.size < window:
return None
else:
# === Add a constant if needed ========================================
X = x.to_frame()
X['c'] = 1
# === Loop... this can be improved ====================================
estimate_data = []
for i in range(window, x.index.size+1):
X_slice = X.values[i-window:i,:] # always index in np as opposed to pandas, much faster
y_slice = y.values[i-window:i]
coeff = np.dot(np.dot(np.linalg.inv(np.dot(X_slice.T, X_slice)), X_slice.T), y_slice)
estimate_data.append(coeff[0] * x.values[window-1] + coeff[1])
# === Assemble ========================================================
estimate = pandas.Series(data=estimate_data, index=x.index[window-1:])
return estimate
Remarques
Dans certains cas spécifiques, qui ne nécessitent que l'estimation finale de la régression, x.rolling(window=60).apply(my_ols)
semble être un peu lent
Pour rappel, les coefficients d'une régression peuvent être calculés sous forme de produit matriciel, comme vous pouvez le lire sur la page page des moindres carrés de wikipedia . Cette approche via la multiplication matricielle de numpy
peut accélérer le processus quelque peu par rapport à l’utilisation des ols dans statsmodels
. Ce produit est exprimé dans la ligne commençant par coeff = ...