
本文详解如何使用 Pandas 的 groupby().diff() 正确计算每个 ID 随时间变化的 qty 差值,避免因分组逻辑错误导致全为 NaN 的常见问题。
本文详解如何使用 pandas 的 `groupby().diff()` 正确计算每个 id 随时间变化的 qty 差值,避免因分组逻辑错误导致全为 nan 的常见问题。
在 Pandas 中,transform() 方法会对每个分组独立应用函数,并返回与原 DataFrame 等长的结果。但若你希望计算「同一 ID 在不同日期间的数量变化」(即时间序列意义上的差分),必须按 id 分组后对 qty 列执行 diff() —— 而不是按 ['date', 'id'] 这种唯一组合分组(后者每组仅含单行,diff() 必然返回 NaN)。
关键在于:变化量(delta)是同一 ID 在时间维度上的前后差异,因此分组键应为 'id',而非包含 'date' 的复合键。同时需确保数据按时间顺序排列,否则 diff() 会基于错误的行序计算。
以下是完整、可复现的解决方案:
import pandas as pd
# 构建示例数据(已按 date 排序)
df = pd.DataFrame({
'date': [*['2020-01-01']*3, *['2020-01-02']*3, *['2020-01-03']*3],
'id': ['A1', 'A2', 'A3']*3,
'qty': [50, 10, 20, 40, 10, 20, 40, 15, 25]
}).sort_values(['id', 'date']).reset_index(drop=True) # 更稳妥:按 id + date 双重排序
# ✅ 正确做法:按 'id' 分组,对 'qty' 计算差分
df['delta'] = df.groupby('id')['qty'].diff()
# 可选:将首行 NaN 替换为 0(或保留 NaN 表示无前值)
df['delta'] = df['delta'].fillna(0).astype(int)
print(df)输出结果:
date id qty delta 0 2020-01-01 A1 50 0 1 2020-01-02 A1 40 -10 2 2020-01-03 A1 40 0 3 2020-01-01 A2 10 0 4 2020-01-02 A2 10 0 5 2020-01-03 A2 15 5 6 2020-01-01 A3 20 0 7 2020-01-02 A3 20 0 8 2020-01-03 A3 25 5
⚠️ 注意事项:
- 排序至关重要:diff() 默认对组内行按原始索引顺序计算差分。若 date 未排序,结果将错乱。推荐显式使用 sort_values(['id', 'date']) 确保时序正确。
- 避免 transform(lambda x: x.diff()) 的误用:该写法虽语法合法,但若分组粒度过细(如 groupby(['date','id'])),每组仅一行,diff() 恒返回 NaN。
- fillna() 的选择:首条记录无前值,diff() 返回 NaN。根据业务含义决定是否填充(如填 0 表示“无变化”,或保留 NaN 表示“不可计算”)。
- 扩展用法:如需计算多期差分(如隔日变化),可用 diff(periods=2);如需百分比变化,可用 pct_change()。
总结:理解 groupby().diff() 的语义——它是在每个分组内部按行顺序做一阶差分——是解决此类问题的核心。分组键应反映「变化发生的主体」(这里是 id),而非「观测快照的标识」(如 date+id)。


















