如何用Pandas高效处理大规模排序有序列表(ROL)数据
大规模排序有序列表(ROL)跨周期对比高效处理方案
问题背景
- 处理大规模**排序有序列表(ROL)**数据,需跨周期比较个体对机构/项目的排名变化
- ROL定义:个体提交的报告,按偏好从高到低(0为最偏好)对机构下的项目排序
- 需执行的操作:对比ROL间的排名变化、识别新增机构/项目等
- 当前困境:原用Pandas+字典处理,50万个体耗时约20小时,子样本甚至耗时28小时,急需提速
原实现代码
import pandas as pd import numpy as np # 生成测试数据集 df = pd.DataFrame([[1, 1, 0, 100, 101], [1, 2, 0, 100, 101], [1, 2, 1, 100, 102], [2, 1, 0, 100, 101], [2, 2, 0, 100, 101], [2, 2, 1, 200, 202], [3, 1, 0, 100, 101], [3, 1, 1, 200, 201], [3, 2, 0, 100, 101], [3, 2, 1, 200, 201], [4, 1, 0, 100, 101], [4, 1, 1, 200, 201], [4, 2, 0, 200, 201], [4, 2, 1, 100, 101] ], columns=['id_individual', 'period', 'rank', 'id_institution', 'id_program']) df['change_app'] = False df['change_order'] = False df['add_newinst'] = False df['add_newprog'] = False for indiv in df['id_individual'].unique(): # 获取个体各周期的排名数据 r_pre = df.loc[(df['id_individual'] == indiv) & (df['period'] == 1)] r_post = df.loc[(df['id_individual'] == indiv) & (df['period'] == 2)] # 生成存储排名的字典 rank_pre = {} rank_post = {} # 提取机构和项目信息存入字典 for i in range(0, len(r_pre)): rank_pre[i] = r_pre['id_institution'].loc[r_pre['rank'] == i].values[0], r_pre['id_program'].loc[r_pre['rank'] == i].values[0] for i in range(0, len(r_post)): rank_post[i] = r_post['id_institution'].loc[r_post['rank'] == i].values[0], r_post['id_program'].loc[r_post['rank'] == i].values[0] # 若前后周期排名字典不同,计算各类变化 if rank_pre != rank_post: # 标记整体申请变化 df['change_app'].loc[(df['id_individual'] == indiv)] = True # 标记是否仅为排序变化(项目/机构集合一致、数量相同) df['change_order'].loc[(df['id_individual'] == indiv)] = (set(rank_pre.values()) == set(rank_post.values())) & (len(rank_pre) == len(rank_post)) # 提取机构和项目集合 programs_pre = set(rank_pre.values()) programs_post = set(rank_post.values()) inst_pre = set([x[0] for x in rank_pre.values()]) inst_post = set([x[0] for x in rank_post.values()]) # 标记是否新增机构 df['add_newinst'].loc[(df['id_individual'] == indiv)] = len(inst_post - inst_pre) > 0 # 标记是否新增项目 df['add_newprog'].loc[(df['id_individual'] == indiv)] = len(programs_post - programs_pre) > 0 df.head(14)
预期输出
id_individual period rank id_institution id_program change_app change_order add_newinst add_newprog 0 1 1 0 100 101 True False False True 1 1 2 0 100 101 True False False True 2 1 2 1 100 102 True False False True 3 2 1 0 100 101 True False True True 4 2 2 0 100 101 True False True True 5 2 2 1 200 202 True False True True 6 3 1 0 100 101 False False False False 7 3 1 1 200 201 False False False False 8 3 2 0 100 101 False False False False 9 3 2 1 200 201 False False False False 10 4 1 0 100 101 True True False False 11 4 1 1 200 201 True True False False 12 4 2 0 200 201 True True False False 13 4 2 1 100 101 True True False False
高效优化方案
原代码的核心问题是嵌套循环+多次切片查询,严重拖慢性能。优化思路是利用Pandas的向量化操作、分组聚合,避免逐个体循环,大幅减少IO操作。
优化后代码
import pandas as pd import numpy as np # 生成测试数据集 df = pd.DataFrame([[1, 1, 0, 100, 101], [1, 2, 0, 100, 101], [1, 2, 1, 100, 102], [2, 1, 0, 100, 101], [2, 2, 0, 100, 101], [2, 2, 1, 200, 202], [3, 1, 0, 100, 101], [3, 1, 1, 200, 201], [3, 2, 0, 100, 101], [3, 2, 1, 200, 201], [4, 1, 0, 100, 101], [4, 1, 1, 200, 201], [4, 2, 0, 200, 201], [4, 2, 1, 100, 101] ], columns=['id_individual', 'period', 'rank', 'id_institution', 'id_program']) # 1. 对每个个体+周期,生成排序后的(机构,项目)元组列表和集合 def aggregate_rol(group): # 按rank升序排序,保证顺序正确 sorted_group = group.sort_values('rank') # 生成有序的(机构,项目)元组列表 rol_list = list(zip(sorted_group['id_institution'], sorted_group['id_program'])) # 生成项目集合、机构集合 prog_set = set(rol_list) inst_set = set(x[0] for x in rol_list) return pd.Series({ 'rol_list': rol_list, 'prog_set': prog_set, 'inst_set': inst_set, 'rol_len': len(rol_list) }) # 分组聚合,得到每个个体各周期的ROL特征 rol_features = df.groupby(['id_individual', 'period']).apply(aggregate_rol).unstack() # 2. 提取前后周期的特征,计算各类变化 rol_features['change_app'] = rol_features['rol_list'][1] != rol_features['rol_list'][2] rol_features['change_order'] = (rol_features['prog_set'][1] == rol_features['prog_set'][2]) & (rol_features['rol_len'][1] == rol_features['rol_len'][2]) & rol_features['change_app'] rol_features['add_newinst'] = rol_features['inst_set'][2].difference(rol_features['inst_set'][1]).apply(len) > 0 rol_features['add_newprog'] = rol_features['prog_set'][2].difference(rol_features['prog_set'][1]).apply(len) > 0 # 3. 将结果合并回原DataFrame result_df = df.merge( rol_features[['change_app', 'change_order', 'add_newinst', 'add_newprog']].reset_index(), on='id_individual', how='left' ) # 重置列名(unstack后列名是多级,合并后需要调整) result_df.columns = [col if not isinstance(col, tuple) else col[0] for col in result_df.columns] print(result_df.head(14))
优化点说明
- 避免逐个体循环:用
groupby.apply一次性完成所有个体的ROL特征提取,利用Pandas的C级优化代替Python级循环 - 向量化对比:直接对聚合后的Series做元素级对比,无需逐个体判断
- 减少重复查询:一次分组聚合即可获取所有需要的集合、列表数据,避免原代码中多次
loc切片查询 - 排序保证一致性:聚合时显式按rank排序,确保ROL顺序的正确性
性能提升效果
该方案将时间复杂度从O(N*M)(N为个体数,M为每个个体的项目数)降低至O(K log K)(K为总数据行数,排序的时间复杂度),对于50万个体的数据集,耗时可压缩至分钟级。
内容的提问来源于stack exchange,提问作者Nano Ochoa
相关产品推荐
相关产品推荐

