Python Pandas 的累积 OLS

Posted

技术标签:

【中文标题】Python Pandas 的累积 OLS【英文标题】:Cumulative OLS with Python Pandas 【发布时间】:2013-02-12 07:04:42 【问题描述】:

我使用的是 Pandas 0.8.1,目前无法更改版本。如果较新的版本将有助于解决以下问题,请在评论而不是答案中注明。此外,这是针对研究复制项目的,因此即使在仅附加一个新数据点后重新运行回归可能很愚蠢(如果数据集很大),我仍然必须这样做。谢谢!

在 Pandas 中,pandas.olswindow_type 参数有一个 rolling 选项,但似乎暗示这需要选择窗口大小或默认使用整个数据样本。我希望以累积方式使用所有数据。

我正在尝试对按日期排序的pandas.DataFrame 运行回归。对于每个索引i,我想使用从最小日期到索引i 的日期的可用数据进行回归。因此,窗口在每次迭代时有效地增长了一个,所有数据都从最早的观察中累积使用,并且没有数据被丢弃到窗口之外。

我已经编写了一个函数(如下),它与 apply 一起使用来执行此操作,但速度慢得令人无法接受。相反,有没有办法使用pandas.ols直接进行这种累积回归?

这里有一些关于我的数据的更多细节。我有一个pandas.DataFrame,其中包含一列标识符、一列日期、一列左侧值和一列右侧值。我想使用groupby根据标识符进行分组,然后对由左侧和右侧变量组成的每个时间段进行累积回归。

这是我可以在标识符分组对象上与apply 一起使用的函数:

def cumulative_ols(
                   data_frame, 
                   lhs_column, 
                   rhs_column, 
                   date_column,
                   min_obs=60
                  ):

    beta_dict = 
    for dt in data_frame[date_column].unique():
        cur_df = data_frame[data_frame[date_column] <= dt]
        obs_count = cur_df[lhs_column].notnull().sum()

        if min_obs <= obs_count:
            beta = pandas.ols(
                              y=cur_df[lhs_column],
                              x=cur_df[rhs_column],
                             ).beta.ix['x']
            ###
        else:
            beta = np.NaN
        ###
        beta_dict[dt] = beta
    ###

    beta_df = pandas.DataFrame(pandas.Series(beta_dict, name="FactorBeta"))
    beta_df.index.name = date_column
    return beta_df

【问题讨论】:

你看过pd.expanding_apply()吗? 这似乎是一个较新的版本,但我一定会看看。谢谢! @EMS 以防您无法升级,expanding_apply 实际上只是语法糖。如果您指定 rolling_apply 的窗口大小是整个集合的长度并且 min_periods 等于 1,那么您将获得相同的扩展窗口行为 pandas 确实提供了可以应用于系列的 cumsum(累积总和)和 cumprod(累积乘积)。如果您可以分解,您可以将您的功能简化为产品和总和,您可以实现您正在尝试做的事情...... 是的,实际上我昨晚大部分时间都在做这个。我将发布我为单变量回归系数创建的函数。令人难以置信的是它的速度有多快。 【参考方案1】:

根据 cmets 中的建议,我创建了自己的函数,该函数可与 apply 一起使用,并依赖于 cumsum 来累积所有需要的项,以从 OLS 单变量回归向量中表达系数。

def cumulative_ols(
                   data_frame,
                   lhs_column,
                   rhs_column,
                   date_column,
                   min_obs=60,
                  ):
    """
    Function to perform a cumulative OLS on a Pandas data frame. It is
    meant to be used with `apply` after grouping the data frame by categories
    and sorting by date, so that the regression below applies to the time
    series of a single category's data and the use of `cumsum` will work    
    appropriately given sorted dates. It is also assumed that the date 
    conventions of the left-hand-side and right-hand-side variables have been 
    arranged by the user to match up with any lagging conventions needed.

    This OLS is implicitly univariate and relies on the simplification to the
    formula:

    Cov(x,y) ~ (1/n)*sum(x*y) - (1/n)*sum(x)*(1/n)*sum(y)
    Var(x)   ~ (1/n)*sum(x^2) - ((1/n)*sum(x))^2
    beta     ~ Cov(x,y) / Var(x)

    and the code makes a further simplification be cancelling one factor 
    of (1/n).

    Notes: one easy improvement is to change the date column to a generic sort
    column since there's no special reason the regressions need to be time-
    series specific.
    """
    data_frame["xy"]         = (data_frame[lhs_column] * data_frame[rhs_column]).fillna(0.0)
    data_frame["x2"]         = (data_frame[rhs_column]**2).fillna(0.0)
    data_frame["yobs"]       = data_frame[lhs_column].notnull().map(int)
    data_frame["xobs"]       = data_frame[rhs_column].notnull().map(int)
    data_frame["cum_yobs"]   = data_frame["yobs"].cumsum()
    data_frame["cum_xobs"]   = data_frame["xobs"].cumsum()
    data_frame["cumsum_xy"]  = data_frame["xy"].cumsum()
    data_frame["cumsum_x2"]  = data_frame["x2"].cumsum()
    data_frame["cumsum_x"]   = data_frame[rhs_column].fillna(0.0).cumsum()
    data_frame["cumsum_y"]   = data_frame[lhs_column].fillna(0.0).cumsum()
    data_frame["cum_cov"]    = data_frame["cumsum_xy"] - (1.0/data_frame["cum_yobs"])*data_frame["cumsum_x"]*data_frame["cumsum_y"]
    data_frame["cum_x_var"]  = data_frame["cumsum_x2"] - (1.0/data_frame["cum_xobs"])*(data_frame["cumsum_x"])**2
    data_frame["FactorBeta"] = data_frame["cum_cov"]/data_frame["cum_x_var"]
    data_frame["FactorBeta"][data_frame["cum_yobs"] < min_obs] = np.NaN
    return data_frame[[date_column, "FactorBeta"]].set_index(date_column)
### End cumulative_ols

我已经在许多测试用例中验证了这与我之前的函数的输出和 NumPy 的 linalg.lstsq 函数的输出相匹配。我还没有对时间进行完整的基准测试,但有趣的是,在我一直在研究的情况下,它快了大约 50 倍。

【讨论】:

以上是关于Python Pandas 的累积 OLS的主要内容,如果未能解决你的问题,请参考以下文章

日期时间范围之间的 Python Pandas 累积列

Pandas Python Groupby 累积和反向

计算 Pandas 中每天重置的累积盘中指标

Python pandas 字典上的月份分割

两个值匹配 pandas 时的累积计数

Pandas 中分组字符串的累积和