Pandas GroupBy性能优化:多层索引DataFrame求和运算提速
从你的测试结果来看,方案1已经是当前最快的(6.8ms),不过我们可以结合你处理的稀疏双层索引DataFrame特性,从几个方向进一步压榨性能,试试下面这些优化方案:
1. 调整过滤与求和顺序:先求和再过滤
原来的dropna(how='all').sum(axis=1)可以改成sum(axis=1).dropna(),因为sum(axis=1)会自动忽略NaN,全NaN的行求和后结果是NaN,直接用dropna()就能过滤掉这些无效行。这样可以避免先逐行检查是否全NaN,而是先做高度优化的向量化求和,再过滤,效率更高:
output = (df_pointer .add(df1_pointer.sum(axis=1) .dropna() .groupby('Entity').sum() .rename(str(trigger)).to_frame(), fill_value=0))
为什么更快? 求和是pandas原生优化的向量化操作,而dropna(how='all')需要逐行遍历所有列判断状态,在稀疏数据场景下,先求和再过滤的计算量更小。
2. 跳过to_frame(),直接用Series合并
你当前流程是把聚合后的Series转成DataFrame再重命名,其实可以直接给Series指定name,然后在add时指定axis=0,省去转DataFrame的对象转换开销:
agg_series = df1_pointer.sum(axis=1).dropna().groupby('Entity').sum() agg_series.name = str(trigger) output = df_pointer.add(agg_series, axis=0, fill_value=0)
为什么更快? 减少了一次临时DataFrame对象的创建与销毁,节省了内存和计算资源。
3. 利用稀疏数据类型优化
因为你的df1_pointer是极为稀疏的(大量NaN和0),可以将其转换为稀疏格式(pandas 1.0+支持SparseArray),稀疏格式只会存储非NaN/非0的有效数据,大幅减少内存占用和计算量:
# 仅需转换一次,后续循环复用即可 df1_sparse = df1_pointer.astype(pd.SparseDtype("float64", 0.0)) # 优化后的聚合操作 output = df_pointer.add( df1_sparse.sum(axis=1).dropna().groupby('Entity').sum() .rename(str(trigger)).to_frame(), fill_value=0 )
为什么更快? 稀疏数据的求和、分组操作只会处理有效数据,避免了对大量无效值的遍历。
4. 提前固化trigger的字符串格式
如果trigger是循环变量(来自trigger_list),可以提前把整个列表转成字符串列表,避免每次在重命名时重复调用str(trigger):
# 提前转换一次 trigger_str_list = [str(t) for t in trigger_list] # 循环中直接使用预转换的字符串 for trigger_str in trigger_str_list: agg_series = df1_pointer.sum(axis=1).dropna().groupby('Entity').sum() agg_series.name = trigger_str df_pointer = df_pointer.add(agg_series, axis=0, fill_value=0)
为什么更快? 减少了循环内的重复字符串转换操作,尤其是当trigger_list较长时,累积效果明显。
额外注意事项
- 避免使用方案3、4的思路:过滤非0值或
unstack都会把稀疏数据转成密集格式,大幅增加计算开销; groupby('Entity')确实比groupby(level=0)更快,直接指定索引名能让pandas更高效地定位索引层级。
内容的提问来源于stack exchange,提问作者CAPSLOCK

