Pandas 滚动回归:循环的替代方案

Posted

技术标签:

【中文标题】Pandas 滚动回归:循环的替代方案【英文标题】:Pandas rolling regression: alternatives to looping 【发布时间】:2017-11-06 21:12:35 【问题描述】:

在已弃用的 stats/ols 模块中,我很好地利用了 pandas 的 MovingOLS 类(来源 here)。不幸的是,它被 pandas 0.20 彻底破坏了。

在我看来,如何以有效方式运行滚动 OLS 回归的问题已被多次提出(例如here),但措辞有点宽泛,没有很好的答案。

这是我的问题:

    我怎样才能最好地模仿熊猫MovingOLS的基本框架?这个类最吸引人的特性是能够将多个方法/属性视为单独的时间序列——即系数、r 平方、t 统计量等,而无需重新运行回归。例如,您可以创建类似model = pd.MovingOLS(y, x) 的内容,然后调用.t_stat.rmse.std_err 等。相反,在下面的示例中,我没有看到强制分别计算每个统计数据的方法。是否有不涉及创建滑动/滚动“块”(步幅)和运行回归/使用线性代数来获取每个模型参数的方法?

    更广泛地说,在 pandas 的底层发生了什么导致 rolling.apply 无法执行更复杂的功能?* 当你创建一个 .rolling 对象时,用外行的话来说,内部发生了什么——它与我在下面循环遍历每个窗口并创建一个高维数组有根本的不同吗?

*即func传递给.apply

必须从 ndarray 输入 *args 和 **kwargs 产生单个值 传递给函数

这是我目前使用的一些样本数据,回归贸易加权美元对利率差和铜价的百分比变化。 (这没有多大意义;只是随机选择这些。)我已将其从基于类的实现中取出,并尝试将其简化为更简单的脚本。

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

【问题讨论】:

作为一种基本的解决方法,我将使用您的循环并一次存储您需要的所有属性和结果。这是我的答案,因为我对熊猫不够了解github.com/statsmodels/statsmodels/issues/… 这是我的想法。我是 Pandas 和 R 的铁杆用户:对于任何与回归相关的内容,请使用 R,而不是 Python。当然,你可以做大约。同样的事情,但 Python 无法匹配 R 包的广度。另外,你有像 broom 这样的包,可以让你很容易地捕捉到你关心的这些回归统计数据,并将它们放入数据框或乳胶表中。 MovingOLS 不工作,还是刚被删除?您能否直接使用 MovingOLS 代码并有效地利用它制作您自己的小库? 它已被完全删除。而且我不想简单地复制具有多个依赖项的 code 的 1400 行。 【参考方案1】:

使用自定义滚动应用功能。

import numpy as np

df['slope'] = df.values.rolling(window=125).apply(lambda x: np.polyfit(np.array(range(0,125)), x, 1)[0], raw=True)

【讨论】:

【参考方案2】:

我创建了一个 ols 模块,旨在模仿 pandas 已弃用的 MovingOLS;是here。

它有三个核心类:

OLS:静态(单窗口)普通最小二乘回归。输出是 NumPy 数组 RollingOLS:滚动(多窗口)普通最小二乘回归。输出是高维 NumPy 数组。 PandasRollingOLS :将 RollingOLS 的结果包装在 pandas Series 和 DataFrames 中。旨在模仿已弃用的 pandas 模块的外观。

请注意,该模块是 package 的一部分(我目前正在将其上传到 PyPi),它需要一个包间导入。

上面的前两个类完全在 NumPy 中实现,主要使用矩阵代数。 RollingOLS 也广泛利用广播。属性在很大程度上模仿 statsmodels 的 OLS RegressionResultsWrapper

一个例子:

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

【讨论】:

这种方法的主要问题是保留所有 OLS 实例需要大量内存。 @user333700 我做了一些重大更改,如果您有兴趣看一看,应该会更利于记忆。

以上是关于Pandas 滚动回归:循环的替代方案的主要内容,如果未能解决你的问题,请参考以下文章

jQuery scrollTop() 在移动浏览器上滚动 DIV 时不起作用,替代方案?

使用 pandas 的滚动窗口计算一天中每个时间的平均值

JS简单实现“滚动到顶部“按钮悬浮效果,在一定高度才显示 - 替代CSS粘性定位position:sticky方案

JS简单实现“滚动到顶部“按钮悬浮效果,在一定高度才显示 - 替代CSS粘性定位position:sticky方案

滚动百分位数 - 熊猫

Pandas 重采样的替代方案