如何提升Pandas中pivot_table()与concat操作的执行效率?
提速你的Pandas透视表合并操作
你的代码运行缓慢的核心原因有两个:一是循环中反复调用pd.concat,每次合并都会复制整个已有DataFrame,随着数据量增大,时间复杂度会攀升至O(n²);二是多次手动切片筛选生成临时DataFrame,额外消耗了大量内存与CPU时间。下面给你几个逐步优化的方案,从易到难,速度提升效果依次增强:
方案1:先收集透视表到列表,最后一次性合并
这是改动最小的优化方案,只需调整循环逻辑,避免每次迭代都执行合并操作:
import pandas as pd import numpy as np from tqdm import tqdm # 初始化空列表存储每个assignid对应的透视表结果 pivot_list = [] for assignid in tqdm(selectedTests['assignids'].unique()): selectedTestsForAssignID = selectedTests[selectedTests['assignids'] == assignid] # 直接对当前assignid下的所有memberid批量生成透视表,省去内层memberid循环 pivot_temp = selectedTestsForAssignID.pivot_table( index=['memberid'], columns=['col1', 'col2'], values='col3', aggfunc=np.max ) pivot_list.append(pivot_temp) # 最后一次性合并所有临时透视表 selectedTestsPivotCorrect = pd.concat(pivot_list, axis=0, join='outer')
提速原因:
- 移除了不必要的内层
memberid循环——pivot_table本身支持按memberid分组处理,直接对整个selectedTestsForAssignID操作就能得到该assignid下所有memberid的结果。 - 用列表存储临时结果,最后一次完成合并,避免了每次迭代都复制大DataFrame的开销,时间复杂度从O(n²)降到O(n)。
方案2:直接对全量数据做透视(最优解)
如果你的最终需求是每个memberid在所有assignid中,每个(col1, col2)组合下取col3的最大值,那完全不需要循环assignid,一步到位即可:
selectedTestsPivotCorrect = selectedTests.pivot_table( index=['memberid'], columns=['col1', 'col2'], values='col3', aggfunc=np.max )
为什么这是最优解?
- Pandas的
pivot_table内部采用了高效的分组聚合算法,比手动循环快几个数量级,同时避免了所有临时DataFrame的生成,内存占用也会大幅降低。 - 若同一个
memberid在不同assignid中有重复的(col1, col2)组合,aggfunc=np.max会自动取所有记录中的最大值,和你原代码的逻辑一致(原代码concat后重复memberid会保留多条记录,若你需要去重聚合,这个方案更符合需求)。
方案3:分块分组处理(应对内存报错)
如果直接对全量数据做透视仍出现内存不足的问题,可以尝试用groupby替代手动切片,进一步优化分块处理的效率:
from tqdm import tqdm pivot_list = [] # 按assignid分组遍历,groupby的切片比手动筛选更高效 for assignid, group in tqdm(selectedTests.groupby('assignids')): pivot_temp = group.pivot_table( index=['memberid'], columns=['col1', 'col2'], values='col3', aggfunc=np.max ) pivot_list.append(pivot_temp) selectedTestsPivotCorrect = pd.concat(pivot_list, axis=0, join='outer')
优势:
groupby会预先对数据做分组优化,比手动selectedTests[selectedTests['assignids']==assignid]的筛选速度更快,减少了重复计算。- 同样采用“列表收集+一次性合并”的逻辑,避免反复concat的性能损耗。
额外提速小技巧
- 优化数据类型:如果
col1、col2是字符串或固定枚举值,可转为category类型,大幅减少内存占用并加速透视:selectedTests['col1'] = selectedTests['col1'].astype('category') selectedTests['col2'] = selectedTests['col2'].astype('category') - 移除进度条(可选):若追求极致速度,
tqdm的进度条会带来微小开销,不需要的话可以直接去掉。 - Dask并行处理(极端场景):如果数据量远超单机内存,可改用Dask DataFrame,它支持并行分块计算,语法与Pandas几乎一致,能突破单机内存限制。
内容的提问来源于stack exchange,提问作者Patthebug
相关产品推荐
相关产品推荐

