如何在Pandas中按单词分组计算增量与百分比变化并优化性能
高效计算分组时序差值与百分比变化(300万+数据集优化)
问题背景
现有数据集df结构如下:
| date | word | total | top |
|---|---|---|---|
| 14/10/2023 | python | 52 | 5 |
| 15/10/2023 | python | 54 | 9 |
| 16/10/2023 | R | 52 | 2 |
| 17/10/2023 | R | 12 | 1 |
| 18/10/2023 | R | 45 | 1 |
需要新增两列:
delta_top:同一word下当前日期与上一日期top列的差值,无前置数据时为NAdelta_total:同一word下当前日期相对上一日期total列的增减百分比,无前置数据时为NA
期望输出:
| date | word | total | top | delta_top | delta_total |
|---|---|---|---|---|---|
| 14/10/2023 | python | 52 | 5 | NA | NA |
| 15/10/2023 | python | 54 | 9 | 4 | 0.037037037 |
| 16/10/2023 | R | 52 | 2 | NA | NA |
| 17/10/2023 | R | 12 | 1 | 1 | -0.769230769 |
| 18/10/2023 | R | 45 | 1 | 0 | 2.75 |
用户原尝试代码(针对300万+数据运行极慢):
df = ( df.assign(date=pd.to_datetime(df['date'], format='%d/%m/%Y')) .sort_values(by=['word', 'date']) .assign(delta_top=lambda x: x['top'] - x.groupby('word')['top'].shift(1), delta_total=lambda x: ((x['total'] - x.groupby('word')['total'].shift(1)) / x.groupby('word')['total'].shift(1)).fillna('NA'))
优化方案
原代码性能瓶颈在于重复执行groupby('word')操作,每次assign中的lambda都会重新分组,对超大数据集产生大量冗余计算。优化核心是减少重复分组,一次性完成移位值计算。
优化后代码
# 先转换日期格式并按分组+日期排序,确保时序逻辑正确 df['date'] = pd.to_datetime(df['date'], format='%d/%m/%Y') df.sort_values(by=['word', 'date'], inplace=True) # 仅执行一次分组,批量计算移位后的值 grouped = df.groupby('word') df['top_shifted'] = grouped['top'].shift(1) df['total_shifted'] = grouped['total'].shift(1) # 计算目标列 df['delta_top'] = df['top'] - df['top_shifted'] df['delta_total'] = (df['total'] - df['total_shifted']) / df['total_shifted'] # 清理临时列,将NaN替换为标准缺失值pd.NA(替代字符串'NA'更符合pandas数据规范) df.drop(columns=['top_shifted', 'total_shifted'], inplace=True) df[['delta_top', 'delta_total']] = df[['delta_top', 'delta_total']].fillna(pd.NA)
额外性能优化建议
- 用
diff()简化差值计算:delta_top可直接用grouped['top'].diff()替代x - x.shift(1),代码更简洁且性能一致:df['delta_top'] = grouped['top'].diff() - 内存优化:若数据集内存占用过高,可将
total、top等列转换为更紧凑的数值类型(如int32),减少内存开销 - 避免链式写法的隐性重复计算:链式写法虽优雅,但大数据场景下易隐藏重复分组/计算,拆分步骤更利于性能管控
内容的提问来源于stack exchange,提问作者R_Student
相关产品推荐
相关产品推荐

