
本文介绍如何使用 pandas 的 rolling 和 eval 方法,为多列数据计算指定窗口大小的滚动比例,例如求两个工厂在最近 n 期销售额中各自的占比,并确保每行比例之和为 1。
本文介绍如何使用 pandas 的 rolling 和 eval 方法,为多列数据计算指定窗口大小的滚动比例,例如求两个工厂在最近 n 期销售额中各自的占比,并确保每行比例之和为 1。
在时间序列或面板数据分析中,常需动态评估多个来源(如不同工厂、渠道或产品线)对整体指标(如总销售额)的相对贡献。这类“滚动比例”本质上是:对每列在滚动窗口内求和,再将某列的窗口和除以所有相关列窗口和之和。
以两列 factory1sales 和 factory2sales 为例,我们希望为每一行计算前两期(不含当前行) 的滚动占比——即使用 closed='left' 的 2 期窗口(等价于取 t-2 和 t-1 两行),然后按列求和并归一化。
正确实现的关键在于:
- 使用 df.rolling(window=2, closed='left').sum() 获取左闭窗口(即不包含当前行)的列和;
- 利用 eval() 在结果 DataFrame 上直接执行字符串表达式,避免手动重复引用列名,提升可读性与可维护性;
- 由于仅有两列且比例互补,第二列比例可由 1 - 第一列比例 推导,既高效又保证数值一致性。
完整代码如下:
import pandas as pd
df = pd.DataFrame({
'factory1sales': [0, 1, 2, 3, 4],
'factory2sales': [5, 6, 7, 8, 9]
})
# 计算左闭滚动窗口(前2期)和,并求 factory1 占比
rolling_sum = df.rolling(2, closed='left').sum()
df['rolling_proportion_factory1'] = rolling_sum.eval('factory1sales / (factory1sales + factory2sales)')
df['rolling_proportion_factory2'] = 1 - df['rolling_proportion_factory1']
运行后输出:
factory1sales factory2sales rolling_proportion_factory1 rolling_proportion_factory2 0 0 5 NaN NaN 1 1 6 NaN NaN 2 2 7 0.083333 0.916667 3 3 8 0.187500 0.812500 4 4 9 0.250000 0.750000
✅ 验证第 4 行(索引 4):
- factory1sales 前两期为 [2, 3] → 和为 5;
- factory2sales 前两期为 [7, 8] → 和为 15;
- 比例 = 5 / (5 + 15) = 0.25,与结果一致。
⚠️ 注意事项:
- closed='left' 是关键参数,确保窗口不包含当前行(若省略,默认为 'both',会引入当前值,导致逻辑偏差);
- 窗口不足时(前 window-1 行),结果自动为 NaN,符合预期;
- 若扩展至三列及以上,不可简单用 1 - col1,而应统一用 col_i / col_sum 计算,并建议使用 div() 或 apply() 配合 axis=1;
- eval() 虽简洁,但在生产环境中若表达式来自用户输入,需注意安全风险;此时可改用 rolling_sum['factory1sales'].div(rolling_sum.sum(axis=1)) 替代。
该方法兼具性能与表达力,是 pandas 中处理多列滚动归一化的推荐实践。










