Pandas分组重采样耗时过长,求问题排查与优化建议
问题分析与优化方案
环境版本
- Pandas版本:1.5.3
- Python版本:3.9.13
问题背景
处理约250k行的时序DataFrame时,按item_id分组后以日频率重采样补全缺失值,耗时长达12分钟,远超预期。
数据结构
原始数据:
| item_id | date | value |
|---|---|---|
| 1 | 2023-01-01 | 1 |
| 1 | 2023-01-03 | 3 |
| 1 | 2023-01-05 | 5 |
| 2 | 2023-01-01 | 1 |
| 2 | 2023-01-03 | 3 |
| 2 | 2023-01-05 | 5 |
预期结果:
| item_id | date | value |
|---|---|---|
| 1 | 2023-01-01 | 1 |
| 1 | 2023-01-02 | NaN |
| 1 | 2023-01-03 | 3 |
| 1 | 2023-01-04 | NaN |
| 1 | 2023-01-05 | 5 |
| 2 | 2023-01-01 | 1 |
| 2 | 2023-01-02 | NaN |
| 2 | 2023-01-03 | 3 |
| 2 | 2023-01-04 | NaN |
| 2 | 2023-01-05 | 5 |
注:需在每个item_id分组内支持min/max等聚合计算,因此排除stack/unstack方案。
原执行代码
# 数据类型清洗与索引设置 df = df.astype({ 'item_id': 'str', 'value': 'int' }) df['date'] = pd.to_datetime(df['date'], format='%Y-%m-%d') df = df.set_index('date') # 重采样操作 df = df.groupby('item_id').resample('D').value.mean()
耗时原因排查
- Pandas版本性能局限:Pandas 1.5.3的
groupby.resample实现存在性能瓶颈,当分组数量较多时,每个分组单独执行重采样的开销会被放大,尤其在处理十万级数据时更为明显。 - 索引设计不合理:仅将
date设为单索引,groupby('item_id')时需要对普通列进行分组,相比多级索引的处理效率更低。 - 冗余聚合操作:原代码使用
mean(),但实际场景中每个item_id+date组合仅存在一条数据,该聚合操作属于无意义的额外计算,增加了耗时。
优化建议
1. 升级Pandas版本(最有效)
Pandas 2.0+版本对groupby.resample进行了大量性能优化,通过向量化操作减少了分组循环的开销。升级后,相同操作的耗时可降低至原来的1/10甚至更低。
2. 使用多级索引+resample(level)替代groupby.resample
将item_id和date设置为多级索引,直接对索引的时间维度进行重采样,避免分组循环的开销:
# 数据清洗与多级索引设置 df['date'] = pd.to_datetime(df['date'], format='%Y-%m-%d') # 若item_id无需字符串类型,保留整数可减少内存开销 # df['item_id'] = df['item_id'].astype('str') df = df.set_index(['item_id', 'date']) # 用level参数直接重采样 df = df.groupby(level='item_id').resample('D', level='date').asfreq()
3. 用asfreq()替代resample+mean
asfreq()是专门用于补全时间序列缺失值的方法,比resample+聚合函数更高效,且完全满足补全需求。若后续需要计算min/max,可在重采样后单独执行:
# 原流程改写为asfreq df = df.astype({'value': 'int'}) df['date'] = pd.to_datetime(df['date'], format='%Y-%m-%d') df = df.set_index('date') # 用asfreq替代mean() df = df.groupby('item_id').resample('D').asfreq() # 若需要计算分组内的min/max,可后续执行 # df['value_min'] = df.groupby('item_id')['value'].transform('min')
4. 减少不必要的数据类型转换
若item_id本身是整数且无特殊需求,无需转换为字符串类型。字符串类型的列会占用更多内存,且分组时的哈希计算效率低于整数类型。
5. 提前过滤无效数据
若数据中存在重复的item_id+date组合,提前用drop_duplicates(['item_id', 'date'])去重,避免后续聚合操作的冗余计算。
内容的提问来源于stack exchange,提问作者Alec Mather
相关产品推荐
相关产品推荐

