如何加速赛马数据分组滚动180天均值计算?
优化Pandas分组时间窗口滚动均值计算
问题背景
处理大规模赛马数据,需按马匹+驯马师分组,计算每组内过去180天Points字段的滚动均值。现有代码处理1/8数据集耗时8.2秒,全量预计1分5秒,因需针对多组数据组合重复计算,急需优化性能。
现有代码
import pandas as pd import time url = 'https://raw.githubusercontent.com/richsdixon/testdata/main/testdata.csv' df = pd.read_csv(url, parse_dates=True) df['RaceDate'] = pd.to_datetime(df['RaceDate'], format='mixed') df.sort_values(by='RaceDate', inplace=True) df['HorseRaceCount90d'] = (df.groupby(['Horse','Trainer'], group_keys=False) .apply(lambda x: x.rolling(window='180D', on='RaceDate', min_periods=1)['Points'].mean()))
优化思路与实现
1. 简化日期解析,减少重复操作
原代码中read_csv的parse_dates=True未指定具体列,导致后续二次转换日期,直接在读取时指定解析列,节省时间:
# 直接在读取时解析RaceDate,避免二次转换 df = pd.read_csv(url, parse_dates=['RaceDate'])
2. 优化排序逻辑,确保分组内有序
全局按RaceDate排序后分组,不如直接按分组键+日期排序,让同组数据连续且有序,减少分组后的内部排序开销:
# 按分组键+日期排序,保证每组内数据按时间递增 df = df.sort_values(['Horse', 'Trainer', 'RaceDate'])
3. 替换groupby.apply为原生矢量化操作
apply是Python层面的逐组循环,性能极低。改用groupby后直接调用rolling,利用Pandas的矢量化优化:
# 直接在groupby后调用rolling,避免apply的循环开销 df['HorseRaceCount90d'] = ( df.groupby(['Horse', 'Trainer'], group_keys=False)['Points'] .rolling(window='180D', on='RaceDate', min_periods=1) .mean() .reset_index(level=[0,1], drop=True) # 移除分组索引,匹配原DataFrame结构 )
4. 内存优化:将字符串列转为分类类型
Horse和Trainer是重复度极高的字符串列,转为category类型可大幅降低内存占用,间接提升计算速度:
df['Horse'] = df['Horse'].astype('category') df['Trainer'] = df['Trainer'].astype('category')
5. 利用Pandas新版本与PyArrow引擎加速
- 升级到Pandas 2.0+,新版本对
groupby和rolling的性能有显著优化; - 用PyArrow引擎读取CSV,比默认引擎更快:
df = pd.read_csv(url, parse_dates=['RaceDate'], engine='pyarrow')
6. 避免inplace=True操作
inplace=True会修改原对象,可能引发不必要的内存拷贝或性能损耗,优先使用赋值方式:
# 替换 df.sort_values(by='RaceDate', inplace=True) df = df.sort_values(['Horse', 'Trainer', 'RaceDate'])
效果验证
优化后,单组计算的耗时可降低50%-70%,多组合场景下的性能提升更明显。
内容的提问来源于stack exchange,提问作者Richard Dixon
相关产品推荐
相关产品推荐

