You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 13:05:27