35 votes

Pandas Rolling Regression: Alternatives to Looping

J'ai eu la bonne utilisation de pandas' MovingOLS classe (source ici) dans le obsolète stats/ols module. Malheureusement, il a été vidé complètement avec les pandas de 0,20.

La question de l'exécution de roulement de la régression MCO de manière efficace a été demandé à plusieurs reprises (ici, par exemple), mais formulé de manière un peu large et à gauche sans une grande réponse, à mon avis.

Voici mes questions:

  1. Comment puis-je mieux imiter le cadre de base de pandas' MovingOLS? La caractéristique la plus attrayante de cette classe a la possibilité d'afficher plusieurs méthodes/attributs de séries chronologiques distinctes--c'est à dire les coefficients de r-squared, des t-statistiques, etc ... sans avoir besoin de ré-exécuter la régression. Par exemple, vous pouvez créer quelque chose comme model = pd.MovingOLS(y, x) puis appelez .t_stat, .rmse, .std_err, et la comme. Dans l'exemple ci-dessous, à l'inverse, je ne vois pas un moyen de contourner être obligé de calculer chaque statistique séparément. Est-il une méthode qui n'implique pas de créer de glissement ou de roulement "blocs" (progrès) et la course des régressions/à l'aide de l'algèbre linéaire pour obtenir les paramètres du modèle pour chaque?

  2. De façon plus générale, ce qui se passe sous le capot dans les pandas qui fait rolling.apply pas en mesure de prendre des fonctions plus complexes?* Lorsque vous créez un .rolling objet, en d'autres termes, ce qui se passe en interne-est-il fondamentalement différent de boucler sur chaque fenêtre et la création d'une plus-dimensions tableau comme je le fais ci-dessous?

*À savoir, func passée de .apply:

Doit produire une valeur unique à partir d'un ndarray d'entrée *args et **kwargs sont passés à la fonction

Voici où j'en suis actuellement à la, avec quelques données, la régression de la variation en pourcentage dans le commerce pondéré du dollar sur les écarts de taux d'intérêt et le prix du cuivre. (Ce qui n'est pas de faire une tonne de sens; juste pris ces au hasard.) Je l'ai pris d'une classe basée sur la mise en œuvre et a essayé de la bande vers le bas pour un simple script.

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

16voto

Brad Solomon Points 11873

J'ai créé un ols module conçu pour imiter des pandas obsolète MovingOLS; il est ici.

Il a trois classes:

  • OLS : statique (guichet unique) ordinaire de la régression des moindres carrés. La sortie sont des tableaux NumPy
  • RollingOLS : de roulement (multi-fenêtre) ordinaire de la régression des moindres carrés. La sortie sont supérieures de la dimension des tableaux NumPy.
  • PandasRollingOLS : encapsule les résultats de l' RollingOLS dans les pandas de la Série & DataFrames. Conçu pour imiter l'apparence de l'obsolète pandas module.

Notez que le module fait partie d'un package (que je suis actuellement dans le processus de téléchargement de PyPi) et il nécessite une inter-paquets à l'importation.

Les deux premières catégories ci-dessus sont mis en œuvre entièrement dans NumPy et principalement l'utilisation du calcul matriciel. RollingOLS prend avantage de la radiodiffusion largement aussi. Attributs largement imiter statsmodels' LO RegressionResultsWrapper.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X