La régression mobile des Pandas: solutions de rechange à la boucle

j'ai obtenu une bonne utilisation de la classe MovingOLS de pandas (source ici ) dans le module déprécié stats/ols . Malheureusement, il a été complètement vidé avec pandas 0.20.

la question de savoir comment exécuter la régression laminoir OLS de manière efficace a été posée à plusieurs reprises ( ici , par exemple), mais formulée un peu largement et laissée sans grande réponse, à mon avis.

Voici mes questions:

  1. comment imiter au mieux le cadre de base des pandas' MovingOLS ? La caractéristique la plus intéressante de cette classe était la possibilité de considérer plusieurs méthodes/attributs comme des séries chronologiques distinctes--c.-à-d. coefficients, R-au carré, t-statistiques, etc. sans avoir besoin de refaire une régression. Par exemple, vous pouvez créer quelque chose comme model = pd.MovingOLS(y, x) et ensuite appeler .t_stat , .rmse , .std_err , et ainsi de suite. Dans le exemple ci-dessous, à l'inverse, Je ne vois pas d'autre solution que d'être forcé de calculer chaque statistique séparément. Y a-t-il une méthode qui n'implique pas la création de "blocs" glissants/roulants (enjambées) et l'exécution de régressions/utilisant l'algèbre linéaire pour obtenir des paramètres de modèle pour chacun?

  2. plus largement, que se passe-t-il sous le capot des pandas qui rend rolling.apply incapable de prendre des fonctions plus complexes?* Lorsque vous créez un objet .rolling , dans layman's en termes, qu'est-ce qui se passe en interne--est-ce fondamentalement différent d'une boucle au-dessus de chaque fenêtre et de la création d'un tableau de dimensions supérieures comme je le fais ci-dessous?

* à savoir, func passé à .apply :

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

voilà où je suis à l'heure actuelle, avec certaines données d'échantillon, régresser les variations en pourcentage du dollar pondéré par le commerce sur les écarts de taux d'intérêt et le prix du cuivre. (Cela ne fait pas une tonne de sens; juste choisi ces Au hasard. Je l'ai retiré d'une implémentation basée sur la classe et j'ai essayé de le démonter dans 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
24
demandé sur Brad Solomon 2017-06-06 04:31:19
la source

1 ответов

j'ai créé un ols module conçu pour imiter pandas' déprécié MovingOLS ; il est ici .

elle comporte trois classes principales:

  • OLS : statique (guichet unique) ordinaire de la régression des moindres carrés. La sortie sont des tableaux NumPy
  • RollingOLS : régression des moindres carrés ordinaires par laminage (fenêtres multiples). La sortie sont des tableaux NumPy de dimension supérieure.
  • PandasRollingOLS : enveloppe les résultats de RollingOLS dans la série pandas & DataFrames. Conçu pour imiter l'apparence du module pandas déprécié.

notez que le module fait partie d'un paquet (qui est maintenant sur PyPI) et il nécessite une importation inter-paquet.

les deux premières classes ci-dessus sont entièrement implémentées en NumPy et utilisent principalement l'algèbre matricielle. RollingOLS prend l'avantage de la diffusion extensive aussi. Les attributs imitent largement les OLS de stats-modèles 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 OLS, RollingOLS, 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
6
répondu Brad Solomon 2018-01-05 17:42:28
la source