Python分组回归后返回元组解包为独立DataFrame的方法
分组回归结果拆分方案
当前groupby.apply返回的Series中,每个元素为「系数Series、残差Series」构成的不等长元组,直接按普通等长元组拆分会触发长度不匹配报错,按以下步骤处理即可得到目标结果:
1. 提取分组系数表
系数为每个分组返回的固定长度结构化结果,直接遍历提取即可自动保留分组Id作为索引,重置索引后得到符合要求的系数表:
# 提取各分组回归系数 coef_df = fit.apply(lambda x: x[0]).reset_index() # 按需重命名列(单自变量场景下即为N×2规格) coef_df = coef_df.rename(columns={'intercept': '截距项', 'x': 'x系数'})
输出结果行数等于分组数N,包含分组Id、截距项、所有自变量的系数估计值,和预期规格完全一致。
2. 提取匹配原数据顺序的残差表
残差长度和每个分组内的原始样本量一致,提取时需要保留原数据行索引,绑定对应分组Id后合并,避免行错位:
resid_collect = [] for group_id, (_, resid) in fit.items(): resid_part = resid.reset_index() resid_part['Id'] = group_id resid_collect.append(resid_part) # 合并为完整残差表 resid_df = pd.concat(resid_collect, ignore_index=True) resid_df = resid_df.rename(columns={0: '残差'}) # 按原数据行索引排序,保证和原始数据集顺序完全对齐 resid_df = resid_df.sort_values('index').drop(columns=['index']).reset_index(drop=True)
输出结果行数等于原始数据集总行数N*T,携带对应Id标识,和原始数据行一一对应。
更高效的优化实现
如果不想对返回结果做二次拆分,可以直接在回归函数中处理结果输出,避免额外遍历开销,同时规避原代码的链式赋值警告:
import numpy as np import pandas as pd import statsmodels.api as sm def regress(data, yvar, xvars): Y = data[yvar] X = data[xvars].copy() X['intercept'] = 1. model_res = sm.OLS(Y, X, missing='drop').fit() # 直接将残差写入当前分组数据,无需后续对齐索引 data['残差'] = model_res.resid # 返回系数行 return pd.Series(model_res.params.to_dict()) # 模拟数据集 N = 5 T = 20 df = pd.DataFrame(pd.Series(range(0, N)), columns = ['Id']) df = df.reindex(np.repeat(df.index, T)).reset_index(drop=True) df['y'] = np.random.normal(0,1, len(df.index)) df['x'] = np.random.normal(0,1, len(df.index)) df['y'] = 0 + 1*df['x'] + np.random.normal(0,1, len(df.index)) # 直接得到系数表 coef_df = df.groupby('Id').apply(regress, 'y', ['x']).reset_index() # 残差已同步写入原数据框,直接提取即可 resid_df = df[['Id', '残差']]
注意:原代码中
X = data[xvars]取的是原数据的视图,直接新增intercept列会触发pandas链式赋值警告,添加.copy()创建副本即可解决该问题。
内容的提问来源于stack exchange,提问作者sleyde
相关产品推荐
相关产品推荐

