web-dev-qa-db-fra.com

Pandas régression continue: alternatives au bouclage

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:

  1. 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?

  2. 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
29
Brad Solomon

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 NumPy
  • RollingOLS: 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
13
Brad Solomon