Python中基于年份的Rolling Regression实现及window参数设置问题
基于年份的滚动回归实现方案
你的核心问题是RollingOLS默认的window参数是按行计数,而非关联Year变量的时间窗口,同时未考虑数据的ID分组逻辑,导致回归结果不符合需求。以下是针对性的解决方法:
数据预处理
首先确保数据按ID和Year升序排列,保证每个个体的时间序列顺序正确:
import matplotlib.pyplot as plt import numpy as np import pandas as pd import statsmodels.api as sm from statsmodels.regression.rolling import RollingOLS d1 = {'ID': [1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6], 'Earnings': [100, 200, 400, 250, 300, 350, 400, 550, 700, 259, 300, 350, 270, 450, 340, 570, 340, 340], 'WC': [20, 40, 35, 55, 60, 65, 30, 28, 32, 45, 60, 52, 23, 54, 45, 87, 54, 65], 'Year': [1995, 1996, 1997, 1996, 1997, 1998, 1995, 1997, 1998, 1996, 1997, 1998, 1995, 1997, 1998, 1996, 1997, 1998], 'F1_Earnings': [120, 220, 420, 280, 530, 670, 780, 210, 950, 100, 120, 430, 780, 210, 950, 100, 120, 430]} df1 = pd.DataFrame(data=d1) # 按ID和年份排序,保证时间序列顺序 df1 = df1.sort_values(['ID', 'Year']).reset_index(drop=True)
方法一:按ID分组的2年滚动回归
针对每个个体(ID)独立做2年窗口的滚动回归,适合个体间数据独立的场景:
def rolling_reg_by_id(group): # 添加回归常数项,statsmodels默认不含截距 X = sm.add_constant(group[['Earnings', 'WC']]) y = group['F1_Earnings'] # 窗口设为2,因已按年份排序,每行对应一年,window=2即代表2年数据 rols = RollingOLS(y, X, window=2) rres = rols.fit() return rres.params # 按ID分组执行滚动回归 rolling_params = df1.groupby('ID', group_keys=False).apply(rolling_reg_by_id) print(rolling_params)
补充:严格连续年份窗口
如果要求必须使用连续的2年数据(跳过缺失年份的观测),需先补全每个ID的所有年份:
# 生成所有ID和年份的组合 all_id_year = pd.MultiIndex.from_product([df1['ID'].unique(), df1['Year'].unique()], names=['ID', 'Year']) # 补全缺失年份,缺失值填充为NaN df_full = df1.set_index(['ID', 'Year']).reindex(all_id_year).reset_index() # 再次执行分组滚动回归,自动忽略含NaN的窗口 rolling_params_full = df_full.groupby('ID', group_keys=False).apply(rolling_reg_by_id) print(rolling_params_full.dropna())
方法二:全局时间窗口回归
若需求是用所有个体的最近2年数据做回归(比如用1997-1998年所有数据预测1998年的F1_Earnings),可使用时间索引的滚动窗口:
# 将年份转为datetime类型,支持时间窗口计算 df1['Year_dt'] = pd.to_datetime(df1['Year'], format='%Y') df_time = df1.set_index('Year_dt') def reg_func(window): X_win = sm.add_constant(window[['Earnings', 'WC']]) y_win = window['F1_Earnings'] model = sm.OLS(y_win, X_win).fit() return pd.Series(model.params, index=['const', 'Earnings', 'WC']) # 2年时间窗口,至少需要2个观测值才执行回归 rolling_params_global = df_time.rolling(window='2Y', min_periods=2).apply(reg_func, raw=False) print(rolling_params_global.dropna())
关键注意点
- 原代码无效的核心原因:直接用
window=2是按行取数,未关联Year变量和ID分组,混合了不同个体、不同年份的数据。 - 常数项:
RollingOLS默认不含截距,必须用sm.add_constant()添加,否则回归结果会缺失截距项。 - 窗口逻辑:按ID分组时,
window=2对应2行(即2年)数据;全局时间窗口则直接用时间字符串定义窗口范围。
内容的提问来源于stack exchange,提问作者ChurrrT
相关产品推荐
相关产品推荐

