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

10 浏览
0 Comments

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

我充分利用了pandas的MovingOLS类(源代码在这里)在已弃用的stats/ols模块中。不幸的是,随着pandas 0.20的发布,该类被完全删除。

关于如何以高效的方式运行滚动OLS回归的问题已经被提出多次(例如在这里),但问题表述得比较宽泛,并未得到令人满意的答案。

以下是我的问题:

  1. 我如何最好地模仿pandas的MovingOLS的基本框架?这个类最吸引人的特点是能够将多个方法/属性视为单独的时间序列,例如系数、R平方、t统计量等,而无需重新运行回归。例如,您可以创建类似于model = pd.MovingOLS(y, x)的东西,然后调用.t_stat、.rmse、.std_err等。相反,在下面的示例中,我看不到避免强制创建滑动/滚动“块”(步幅)和为每个块运行回归/使用线性代数来获取模型参数的方法。是否有一种不涉及创建滑动/滚动“块”(步幅)和为每个块运行回归/使用线性代数来获取模型参数的方法?
  2. 更广泛地说,是什么让pandas的rolling.apply方法不能接受更复杂的函数?当您创建一个.rolling对象时,在非专业术语中,内部发生了什么,它是否与循环遍历每个窗口并创建更高维数组的方法有根本区别?

*即传递给.apply的func:

必须从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

0