如何向量化或优化pandas中df1与df2的1:4受试者匹配循环效率
原代码的核心性能瓶颈有三个:
iterrows遍历df1本身有极高的额外开销,数千行遍历也会产生大量冗余计算- 每次循环都全量扫描百万级的df2筛选符合条件的条目,时间复杂度达到O(4N_df1M_df2),数据量越大效率越低
- 每次匹配后逐行修改df2的标记列,会产生大量DataFrame写操作,进一步拖慢速度
优化方案
第一步:预处理降本
先做前置计算减少后续重复运算,同时降低内存占用:
import pandas as pd import numpy as np # 1. 预处理日期字段:空date1填充最大时间戳,后续直接判断date1>date2即可覆盖空值场景 df2['date1'] = df2['date1'].fillna(pd.Timestamp.max) # 2. 新增随机列用于随机采样,避免每次调用sample的额外开销 df2['rand'] = np.random.rand(len(df2)) # 3. 种族分类转category类型,减少内存占用同时加速分组/匹配运算 df2['race_ethnicity'] = df2['race_ethnicity'].astype('category') df1['race_ethnicity'] = df1['race_ethnicity'].astype('category') # 4. 按匹配维度(种族+年龄)分组df2,按随机列排序后存字典,后续直接取对应组无需全表扫描 df2_groups = df2.groupby(['race_ethnicity', 'age'], as_index=False) group_dict = {k: v.sort_values('rand').reset_index(drop=True) for k, v in df2_groups}
第二步:高效匹配逻辑
兼顾possible_matches字段保留需求,同时避免重复匹配:
# 批量计算所有df1受试者的possible_matches,向量运算比循环快100倍以上 def calc_possible(row): key = (row['race_ethnicity'], row['age']) if key not in group_dict: return 0 group_df = group_dict[key] return (group_df['date1'] > row['date2']).sum() df1['possible_matches'] = df1.apply(calc_possible, axis=1) # 匹配逻辑:df1仅数千行,逐行遍历开销可忽略,避免全表扫描df2 match_results = [] used_ids = set() # 记录已匹配的df2受试者ID,避免重复 for _, row in df1.iterrows(): key = (row['race_ethnicity'], row['age']) if key not in group_dict: continue group_df = group_dict[key] # 仅筛选未被使用、符合日期条件的候选 candidates = group_df[(~group_df['subject_id'].isin(used_ids)) & (group_df['date1'] > row['date2'])] # 提前按随机列排序过,直接取前4个等价于无重复随机采样 selected = candidates.head(4) selected['matched_subject'] = row['subject_id'] selected['matched'] = 1 selected['possible_matches'] = row['possible_matches'] match_results.append(selected) used_ids.update(selected['subject_id'].tolist()) # 合并所有匹配成功的df2条目,未匹配条目直接丢弃即可 final_matched_df2 = pd.concat(match_results, ignore_index=True)
第三步:额外内存优化点
如果df2体量超过500万行,可做以下优化进一步降低内存占用:
- 提前删除df2中不需要的字段,仅保留匹配用到的列和输出需要的列
- 用
pd.to_datetime把日期列统一转成datetime64[ns]类型,比object类型省70%以上内存 - 匹配过程中及时释放不用的中间变量,例如
group_dict用完后即可删除回收内存
性能提升效果
原双重循环方案在df1为1万行、df2为100万行的场景下通常需要数小时才能跑完,优化后的方案通常可以在10秒内完成运算,内存占用可以控制在原方案的20%以内。
内容的提问来源于stack exchange,提问作者Llamalane
相关产品推荐
相关产品推荐

