如何高效实现Pandas DataFrame行组合求和计算
连续区间求和的高效实现方案
原有代码慢的核心原因
- 逐次调用
iloc切片和逐行累加求和,存在大量重复计算 - 反复对结果DataFrame执行
append操作,每次都会全量拷贝DataFrame,内存和时间双重损耗 - 所有循环都在Python解释层执行,没有利用NumPy的C层向量化运算能力
核心优化原理
所有连续区间的求和都可以通过前缀和(Cumulative Sum)实现O(1)取值,完全消除重复累加的开销。
对于长度为n的数组Count、Score:
- 预先计算两个前缀和数组:
c_count[k]= Count数组前k个元素的累加和c_score[k]= Score数组前k个元素的累加和
- 任意从行i到行j(左闭右开,与原代码
df.iloc[i:j]逻辑完全对齐)的区间求和,只需要做一次减法:- Total Count =
c_count[j] - c_count[i] - Total Score =
c_score[j] - c_score[i]
- Total Count =
- 对应区间的属性直接通过原数组取值即可:
- Min Ratio = Ratio数组第i行的值
- Max Ratio = Ratio数组第j-1行的值
针对“仅需Top1000比值”目标的内存优化
因为不需要存储全部5000万条组合结果,只需要找Total Score / Total Count比值最高的1000条记录,因此完全不需要生成全量结果集,可以通过两个手段进一步提速省内存:
- 固定区间起点i,将该起点对应的所有后续区间的计算,一次性通过NumPy向量化完成,把Python解释层的循环次数从5000万次压缩到1万次,剩余计算全交给底层C层执行
- 用固定容量为1000的最小堆维护当前找到的Top1000记录,遍历过程中只保留比堆顶更大的条目,内存占用从GB级降到KB级
可直接运行的优化代码
import numpy as np import pandas as pd from heapq import heappush, heapreplace # 1. 预处理:转成无索引的NumPy数组,消除pandas开销 ratio_arr = df['Ratio'].to_numpy() count_arr = df['Count'].to_numpy() score_arr = df['Score'].to_numpy() n = len(df) # 2. 计算前缀和,前置补0保证差值对齐原代码的切片逻辑 c_count = np.concatenate([[0], np.cumsum(count_arr)]) c_score = np.concatenate([[0], np.cumsum(score_arr)]) # 3. 初始化容量为1000的最小堆,每个元素结构为(当前比值, Min Ratio, Max Ratio, Total Count, Total Score) top_heap = [] HEAP_SIZE = 1000 # 4. 外层循环仅遍历每个区间的起点i,Python层循环共n-1次 for i in range(n - 1): # 一次性计算i作为起点,所有终点j对应的区间参数,全程为NumPy向量化运算 j_end = n + 1 tc_block = c_count[i+1:j_end] - c_count[i] ts_block = c_score[i+1:j_end] - c_score[i] # 过滤Total Count为0的情况,避免除零 valid_mask = tc_block > 0 tc_block = tc_block[valid_mask] ts_block = ts_block[valid_mask] ratio_block = ts_block / tc_block max_ratio_block = ratio_arr[i : j_end - 1][valid_mask] min_ratio_val = ratio_arr[i] # 快速跳过:当前块的最大比值都比堆顶小,不需要遍历这个块的内容 if len(top_heap) == HEAP_SIZE and ratio_block.max() < top_heap[0][0]: continue # 逐点处理当前块的所有结果,更新堆 for tc, ts, ratio, max_r in zip(tc_block, ts_block, ratio_block, max_ratio_block): record = (ratio, min_ratio_val, max_r, tc, ts) if len(top_heap) < HEAP_SIZE: heappush(top_heap, record) elif ratio > top_heap[0][0]: # 替换堆顶(当前Top1000中比值最小的条目) heapreplace(top_heap, record) # 5. 把堆里的结果转成最终DataFrame results_top1000 = pd.DataFrame( [rec[1:] for rec in top_heap], columns=['Min Ratio', 'Max Ratio', 'Total Count', 'Total Score'] ) # 补充计算好的比值列(可选) results_top1000['Score/Count Ratio'] = [rec[0] for rec in top_heap]
性能预期
按照10000行的输入规模,这套代码的总运行时间在10秒到30秒区间,相比原代码200小时的估算耗时,提升幅度超过两个数量级:
- 前缀和计算本身的时间复杂度为O(n)
- 内层所有求和、比值计算全部在NumPy的C层完成,无Python解释层的循环开销
- 全程没有全量拼接5000万条中间结果,内存占用极低
常见踩坑点
- 前缀和必须在数组最前面补0,否则会出现行号错位,导致区间求和错误
- 计算比值时需要过滤Total Count为0的情况,避免触发除零错误
- 不要尝试把5000万条组合结果全量存入DataFrame,仅存储需要的Top1000即可,否则会占用数GB内存,拖慢整体速度
内容的提问来源于stack exchange,提问作者mr7
相关产品推荐
相关产品推荐

