Python如何正确拼接滚动回归结果 修复concat产生NaN值问题
滚动OLS回归结果DataFrame拼接异常修复
问题场景
针对全量股票ticker逐标的计算滚动窗口下的OLS回归Beta系数、标准误,循环内单ticker结果生成后直接纵向拼接至总结果表,最终输出表存在大量空值:同一滚动窗口截止日期对应多行,每行仅存储单个ticker的计算结果,其余列均为NaN,无法得到单窗口单行、各列存储不同ticker指标的宽表结构。
错误实现代码
import pandas as pd import numpy as np import statsmodels.api as sm # 模拟基础数据:日频收益率,索引为交易日期,列为各股票ticker np.random.seed(42) trade_dates = pd.date_range(start='2023-01-01', periods=200, freq='B') ticker_list = ['AAPL', 'MSFT', 'GOOG'] stock_ret = pd.DataFrame( np.random.randn(200, 3)/100, index=trade_dates, columns=ticker_list ) mkt_ret = pd.Series( np.random.randn(200)/100, index=trade_dates, name='market_ret' ) roll_window = 60 # 错误拼接逻辑 final_result = pd.DataFrame() for ticker in ticker_list: ticker_calc_res = [] # 逐滚动窗口计算回归指标 for idx in range(roll_window, len(stock_ret)+1): win_stock = stock_ret[ticker].iloc[idx-roll_window:idx] win_mkt = mkt_ret.iloc[idx-roll_window:idx] ols_model = sm.OLS(win_stock, sm.add_constant(win_mkt)).fit() ticker_calc_res.append({ 'date': stock_ret.index[idx-1], f'{ticker}_beta': ols_model.params['market_ret'], f'{ticker}_se': ols_model.bse['market_ret'] }) ticker_df = pd.DataFrame(ticker_calc_res).set_index('date') # 错误点:默认沿纵向axis=0拼接 final_result = pd.concat([final_result, ticker_df], axis=0)
异常输出特征
输出表总长度为 ticker数量 * 有效滚动窗口数,同一日期重复出现多次,非当前循环ticker的字段全部为NaN,样例如下:
| date | AAPL_beta | AAPL_se | MSFT_beta | MSFT_se | GOOG_beta | GOOG_se |
|---|---|---|---|---|---|---|
| 2023-03-28 | 0.124 | 0.089 | NaN | NaN | NaN | NaN |
| 2023-03-29 | 0.117 | 0.091 | NaN | NaN | NaN | NaN |
| 2023-03-28 | NaN | NaN | -0.072 | 0.103 | NaN | NaN |
| 2023-03-29 | NaN | NaN | -0.068 | 0.102 | NaN | NaN |
| 2023-03-28 | NaN | NaN | NaN | NaN | 0.095 | 0.078 |
| 2023-03-29 | NaN | NaN | NaN | NaN | 0.097 | 0.077 |
异常原因:单ticker结果表以日期为索引,不同ticker的结果列名完全不重叠,纵向拼接会按行堆叠,缺失列自动填充NaN。
修复方案
调整拼接逻辑,先将所有单ticker结果存入列表,最终沿横向axis=1按日期索引对齐拼接,自动匹配同一窗口日期下不同ticker的计算值。
修复后可直接运行的代码如下:
final_result = [] for ticker in ticker_list: ticker_calc_res = [] for idx in range(roll_window, len(stock_ret)+1): win_stock = stock_ret[ticker].iloc[idx-roll_window:idx] win_mkt = mkt_ret.iloc[idx-roll_window:idx] ols_model = sm.OLS(win_stock, sm.add_constant(win_mkt)).fit() ticker_calc_res.append({ 'date': stock_ret.index[idx-1], f'{ticker}_beta': ols_model.params['market_ret'], f'{ticker}_se': ols_model.bse['market_ret'] }) ticker_df = pd.DataFrame(ticker_calc_res).set_index('date') final_result.append(ticker_df) # 修正点:沿横向axis=1按索引对齐拼接 final_result = pd.concat(final_result, axis=1).reset_index()
修复后验证
输出表总长度等于有效滚动窗口数,同一日期仅对应单行,所有ticker的指标按列填充无空值,符合预期宽表结构:
| date | AAPL_beta | AAPL_se | MSFT_beta | MSFT_se | GOOG_beta | GOOG_se |
|---|---|---|---|---|---|---|
| 2023-03-28 | 0.124 | 0.089 | -0.072 | 0.103 | 0.095 | 0.078 |
| 2023-03-29 | 0.117 | 0.091 | -0.068 | 0.102 | 0.097 | 0.077 |
内容的提问来源于stack exchange,提问作者Trevor Seibert
相关产品推荐
相关产品推荐

