
本文介绍如何使用 DataFrame.rolling() 和 eval() 高效计算多列之间的滚动比例(如各工厂销售额占滚动窗口内总销售额的比例),支持任意窗口大小和列数扩展。
本文介绍如何使用 `dataframe.rolling()` 和 `eval()` 高效计算多列之间的滚动比例(如各工厂销售额占滚动窗口内总销售额的比例),支持任意窗口大小和列数扩展。
在数据分析中,常需基于时间或行序对多列进行滚动聚合,并进一步计算相对占比(例如:某工厂在最近 N 期总销售额中的贡献比例)。Pandas 提供了灵活的 rolling() 接口,结合 eval() 可以简洁、可读性强地实现此类计算。
以两个工厂的销售数据为例:
import pandas as pd
df = pd.DataFrame({
'factory1sales': [0, 1, 2, 3, 4],
'factory2sales': [5, 6, 7, 8, 9]
})
目标是:对每行,基于左侧闭合的 2 行窗口(即不包含当前行,仅取前两行),计算 factory1sales 与 factory2sales 在该窗口内的滚动和,并求出 factory1 占比(即 sum(factory1)/sum(factory1 + factory2))。
关键点在于:
- 使用 closed='left' 确保窗口仅包含历史值(避免数据泄露);
- rolling(2).sum() 返回一个与原 DataFrame 形状一致的滚动和 DataFrame;
- eval() 支持在结果 DataFrame 上直接执行列级表达式,无需手动提取 Series。
✅ 正确实现如下:
# 计算滚动比例(factory1 占比)
rolling_sum = df.rolling(2, closed='left').sum()
df['rolling_proportion_factory1'] = rolling_sum.eval('factory1sales / (factory1sales + factory2sales)')
# 利用互补性快速得到 factory2 占比(二者和恒为 1)
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)→ 2 + 3 = 5
- factory2sales 的滚动和(前两行:索引 2+3)→ 7 + 8 = 15
- 总和 = 5 + 15 = 20 → 比例 = 5 / 20 = 0.25 ✅
⚠️ 注意事项:
- 窗口首 window−1 行无法形成完整窗口,返回 NaN,符合预期;
- 若需包含当前行,改用 closed='both'(默认)并调整逻辑;
- 对于更多列(如 factory3sales),可扩展 eval 表达式为 'factory1sales / (factory1sales + factory2sales + factory3sales)';
- eval() 在大数据集上性能优异,但若需动态列名,建议改用 .sum(axis=1) 配合布尔索引。
总结:该方法兼顾简洁性、可维护性与扩展性,是处理多列滚动占比问题的推荐范式。










