如何用Pandas滚动窗口计算个股CAPE比率?需多列数据参与运算
计算个股CAPE比率的解决方案
问题背景
需要基于包含季度EPS(每股收益)和CPI数据的DataFrame,计算10年(40个季度)通胀调整平均市盈率(CAPE)。计算逻辑为:对每一行,回溯40个周期,用窗口起始期的CPI除以窗口内各期的CPI得到比率,将该比率与对应期EPS相乘后取平均值。使用rolling+agg时因需同时调用两列数据受阻,且认为辅助列无法解决窗口动态值问题。
示例数据构造代码:
import pandas as pd df = pd.DataFrame(index=range(100)) df['CPI Value'] = 1. + (df.index / 100.) df['EPS'] = 10.
错误尝试代码:
df['CAPE'] = df.rolling(40).agg(lambda x: ((x['CPI Value'][0] / x['CPI Value']) * x['EPS']).mean())
解决方案
方法1:使用rolling.apply(支持多列自定义计算)
rolling.agg默认仅处理单列数据,而rolling.apply可以接收整个窗口的DataFrame,满足多列联动计算需求:
def calculate_cape(window): # 获取窗口起始期的CPI值 base_cpi = window['CPI Value'].iloc[0] # 计算通胀调整后的EPS并取平均值 adjusted_eps = (base_cpi / window['CPI Value']) * window['EPS'] return adjusted_eps.mean() # 滑动窗口大小设为40,min_periods=40确保仅窗口满40个周期时才计算 df['CAPE'] = df.rolling(window=40, min_periods=40).apply(calculate_cape, raw=False)
raw=False是核心配置:让apply传入窗口对应的DataFrame对象,而非NumPy数组,可直接通过列名访问数据。min_periods=40避免窗口未满时生成无效值,符合10年数据的计算要求。
方法2:向量化计算(性能更优)
如果数据量较大,apply的循环效率较低,可以用向量化方式实现:
# 生成CPI的滑动窗口矩阵(每行对应一个窗口的CPI值) cpi_window = df['CPI Value'].rolling(40).apply(lambda x: x, raw=True).values # 生成EPS的滑动窗口矩阵 eps_window = df['EPS'].rolling(40).apply(lambda x: x, raw=True).values # 提取每个窗口的第一个CPI值(起始期CPI) base_cpi = cpi_window[:, 0].reshape(-1, 1) # 计算通胀调整后的EPS平均值 adjusted_eps_mean = ((base_cpi / cpi_window) * eps_window).mean(axis=1) # 赋值给CAPE列,前39行设为缺失值(窗口未满) df['CAPE'] = pd.Series(adjusted_eps_mean, index=df.index) df['CAPE'].iloc[:39] = pd.NA
这种方式用矩阵运算替代循环,在大数据集上速度更快,但需注意内存占用(窗口矩阵大小为总行数×40)。
关键说明
原错误的核心是rolling.agg的lambda函数只能接收单列数据,无法同时访问CPI Value和EPS两列;而rolling.apply可以传入整个窗口的DataFrame,完美解决了多列联动计算的问题。两种方法均严格遵循CAPE的计算逻辑:回溯40个周期,用窗口起始CPI做通胀调整,再取平均值。
内容的提问来源于stack exchange,提问作者kozor1
相关产品推荐
相关产品推荐

