J'ai bien utilisé la classe MovingOLS
de pandas (source ici ) dans le module stats/ols
Déconseillé. Malheureusement, il a été complètement vidé avec pandas 0.20.
La question de savoir comment exécuter la régression OLS roulante de manière efficace a été posée plusieurs fois ( ici , par exemple), mais formulée un peu largement et laissée sans grande réponse, à mon avis.
Voici mes questions:
Comment puis-je imiter au mieux le cadre de base du pandas MovingOLS
? La caractéristique la plus attrayante de cette classe était la possibilité de visualiser plusieurs méthodes/attributs comme des séries chronologiques distinctes, c'est-à-dire. coefficients, r-carré, statistiques t, etc. sans avoir besoin de relancer la régression. Par exemple, vous pouvez créer quelque chose comme model = pd.MovingOLS(y, x)
puis appeler .t_stat
, .rmse
, .std_err
, Etc. Dans l'exemple ci-dessous, à l'inverse, je ne vois pas comment contourner l'obligation de calculer chaque statistique séparément. Existe-t-il une méthode qui n'implique pas la création de "blocs" glissants/roulants (foulées) et l'exécution de régressions/l'utilisation d'algèbre linéaire pour obtenir les paramètres du modèle pour chacun?
Plus largement, que se passe-t-il sous le capot dans pandas qui empêche rolling.apply
De prendre des fonctions plus complexes? * Lorsque vous créez un objet .rolling
, Dans termes de profane, que se passe-t-il en interne - est-ce fondamentalement différent de boucler sur chaque fenêtre et de créer un tableau de dimension supérieure comme je le fais ci-dessous?
* A savoir, func
passé à .apply
:
Doit produire une valeur unique à partir d'une entrée ndarray * args et ** kwargs sont passés à la fonction
Voici où j'en suis actuellement avec quelques exemples de données, régressant les variations en pourcentage du dollar pondéré par les échanges sur les écarts de taux d'intérêt et le prix du cuivre. (Cela n'a pas beaucoup de sens; je les ai simplement choisis au hasard.) Je l'ai retiré d'une implémentation basée sur une classe et j'ai essayé de le réduire à un script plus simple.
from datetime import date
from pandas_datareader.data import DataReader
import statsmodels.formula.api as smf
syms = {'TWEXBMTH' : 'usd',
'T10Y2YM' : 'term_spread',
'PCOPPUSDM' : 'copper'
}
start = date(2000, 1, 1)
data = (DataReader(syms.keys(), 'fred', start)
.pct_change()
.dropna())
data = data.rename(columns = syms)
data = data.assign(intercept = 1.) # required by statsmodels OLS
def sliding_windows(x, window):
"""Create rolling/sliding windows of length ~window~.
Given an array of shape (y, z), it will return "blocks" of shape
(x - window + 1, window, z)."""
return np.array([x[i:i + window] for i
in range(0, x.shape[0] - window + 1)])
data.head(3)
Out[33]:
usd term_spread copper intercept
DATE
2000-02-01 0.012573 -1.409091 -0.019972 1.0
2000-03-01 -0.000079 2.000000 -0.037202 1.0
2000-04-01 0.005642 0.518519 -0.033275 1.0
window = 36
wins = sliding_windows(data.values, window=window)
y, x = wins[:, :, 0], wins[:, :, 1:]
coefs = []
for endog, exog in Zip(y, x):
model = smf.OLS(endog, exog).fit()
# The full set of model attributes gets lost with each loop
coefs.append(model.params)
df = pd.DataFrame(coefs, columns=data.iloc[:, 1:].columns,
index=data.index[window - 1:])
df.head(3) # rolling 36m coefficients
Out[70]:
term_spread copper intercept
DATE
2003-01-01 -0.000122 -0.018426 0.001937
2003-02-01 0.000391 -0.015740 0.001597
2003-03-01 0.000655 -0.016811 0.001546
J'ai créé un module ols
conçu pour imiter les MovingOLS
obsolètes des pandas; c'est ici .
Il a trois classes principales:
OLS
: régression des moindres carrés ordinaires statiques (à fenêtre unique). Les résultats sont des tableaux NumPyRollingOLS
: roulement (multi-fenêtre) régression des moindres carrés ordinaires. Les résultats sont des tableaux NumPy de dimension supérieure.PandasRollingOLS
: encapsule les résultats de RollingOLS
dans pandas Series & DataFrames. Conçu pour imiter l'apparence du pandas) module.Notez que le module fait partie d'un package (que je suis actuellement 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 implémentées entièrement dans NumPy et utilisent principalement l'algèbre matricielle. RollingOLS
profite également d'une large diffusion. Les attributs imitent largement l'OLS RegressionResultsWrapper
de statsmodels.
Un exemple:
import urllib.parse
import pandas as pd
from pyfinance.ols import PandasRollingOLS
# You can also do this with pandas-datareader; here's the hard way
url = "https://fred.stlouisfed.org/graph/fredgraph.csv"
syms = {
"TWEXBMTH" : "usd",
"T10Y2YM" : "term_spread",
"GOLDAMGBD228NLBM" : "gold",
}
params = {
"fq": "Monthly,Monthly,Monthly",
"id": ",".join(syms.keys()),
"cosd": "2000-01-01",
"coed": "2019-02-01",
}
data = pd.read_csv(
url + "?" + urllib.parse.urlencode(params, safe=","),
na_values={"."},
parse_dates=["DATE"],
index_col=0
).pct_change().dropna().rename(columns=syms)
print(data.head())
# usd term_spread gold
# DATE
# 2000-02-01 0.012580 -1.409091 0.057152
# 2000-03-01 -0.000113 2.000000 -0.047034
# 2000-04-01 0.005634 0.518519 -0.023520
# 2000-05-01 0.022017 -0.097561 -0.016675
# 2000-06-01 -0.010116 0.027027 0.036599
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 gold
# DATE
# 2001-01-01 0.000033 -0.054261
# 2001-02-01 0.000277 -0.188556
# 2001-03-01 0.002432 -0.294865
# 2001-04-01 0.002796 -0.334880
# 2001-05-01 0.002448 -0.241902
print(model.fstat.head())
# DATE
# 2001-01-01 0.136991
# 2001-02-01 1.233794
# 2001-03-01 3.053000
# 2001-04-01 3.997486
# 2001-05-01 3.855118
# Name: fstat, dtype: float64
print(model.rsq.head()) # R-squared
# DATE
# 2001-01-01 0.029543
# 2001-02-01 0.215179
# 2001-03-01 0.404210
# 2001-04-01 0.470432
# 2001-05-01 0.461408
# Name: rsq, dtype: float64