使用Python Pandas的累积OLS

时间:2013-02-26 17:44:15

标签: python optimization pandas regression

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

在Pandas中,对rolling的{​​{1}}参数有一个window_type选项,但似乎暗示这需要选择窗口大小或使用整个数据样本作为默认。我希望以累积的方式使用所有数据。

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

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

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

以下是我能够在标识符分组对象上使用groupby的函数:

apply

1 个答案:

答案 0 :(得分:0)

根据评论中的建议,我创建了自己的函数,可以与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倍。