pandas GroupBy.agg是否高效?求大规模数据集分组计算优化方案
问题:Pandas大数据集分组聚合性能优化
我的Pandas数据处理任务如下:
- 将DataFrame的
V列拆分为数值部分V_u与限定符V_c(仅包含<、>或=); - 按
S、M分组后完成以下聚合操作:- 计算
V_u的均值V_mean、样本量V_N、样本标准差V_sample_sd - 生成分组内唯一ID的逗号分隔列表
- 找出
V_c中出现最频繁的限定符V_mean_c - 对
V_mean应用与M映射的函数得到TV_mean - 给
V_mean和TV_mean添加对应限定符V_mean_c
- 计算
目前已实现的方案:
- 拆分
V列时,用map+循环的方式比apply更高效; - 分组聚合使用
GroupBy.agg,但在45万条记录的数据集上耗时约1分钟,后续要处理更大数据集,急需更高效的优化方案,测试过其他方案但性能更差。
优化方案
一、用内置矢量化聚合替代自定义函数
GroupBy.agg里的自定义函数是性能杀手,尽量用Pandas内置的矢量化方法:
- 统计类指标直接调用内置方法,避免重复分组:
# 先计算基础统计量 stats_df = df.groupby(['S', 'M']).agg( V_mean=('V_u', 'mean'), V_N=('V_u', 'count'), V_sample_sd=('V_u', 'std', ddof=1) # ddof=1对应样本标准差 ).reset_index() - 最频繁限定符
V_mean_c,利用value_counts默认降序的特性,取第一个结果:mode_c = df.groupby(['S', 'M'])['V_c'].apply(lambda x: x.value_counts().index[0]).reset_index(name='V_mean_c') stats_df = stats_df.merge(mode_c, on=['S', 'M'], how='left')
二、开启PyArrow分组引擎(Pandas 2.0+)
如果你的Pandas版本在2.0及以上,用PyArrow作为分组引擎能显著提速:
# 分组时指定engine参数 df.groupby(['S', 'M'], engine='pyarrow').agg(...)
先确保装了PyArrow:pip install pyarrow
三、优化唯一ID的拼接逻辑
ID拼接是常见性能瓶颈,尽量减少循环操作:
- 先对ID去重,再用
str.cat矢量化拼接:# 先去重避免重复ID,再分组拼接 unique_ids = df.drop_duplicates(['S', 'M', 'ID']).groupby(['S', 'M'])['ID'].apply(lambda x: ','.join(x.astype(str))).reset_index(name='unique_ids') stats_df = stats_df.merge(unique_ids, on=['S', 'M'], how='left') - 如果ID是数值型,提前转成字符串,减少分组内的类型转换开销。
四、预映射M对应的函数,减少分组内计算
如果M的取值有限,先把M对应的函数结果预计算好,再做分组聚合,避免在分组内重复调用函数:
# 假设M_to_func是存储M与对应函数的字典 # 提前计算映射后的值,再结合V_mean得到TV_mean stats_df['TV_mean'] = stats_df['M'].map(M_to_func) * stats_df['V_mean']
把函数调用从分组内转移到全局矢量化操作,能大幅降低计算耗时。
五、用Dask处理超大规模数据
如果数据集大到单机Pandas扛不住,试试Dask DataFrame,它兼容Pandas API,能利用多核心甚至集群资源并行计算:
import dask.dataframe as dd # 读取大文件为Dask DataFrame ddf = dd.read_csv('your_large_data.csv') # 拆分V列(用矢量化的str.extract) ddf[['V_c', 'V_u']] = ddf['V'].str.extract(r'([<>]?=?)(\d+\.?\d*)', expand=True) ddf['V_u'] = ddf['V_u'].astype(float) # 分组聚合,最后用compute()触发计算 result = ddf.groupby(['S', 'M']).agg({ 'V_u': ['mean', 'count', 'std'], 'ID': lambda x: x.unique().str.cat(sep=','), 'V_c': lambda x: x.value_counts().index[0] }).compute()
内容的提问来源于stack exchange,提问作者user6376297
相关产品推荐
相关产品推荐

