Pandas禁用~与not in实现两DataFrame匹配标记Status列
问题场景
现有两个Pandas DataFrame,样例数据与读取代码如下:
- df1 样例数据:
ID,Name,Sub,Country 1,ABC,ENG,UK 1,ABC,MATHS,UK 1,ABC,Science,UK 2,ABE,ENG,USA 2,ABE,MATHS,USA 2,ABE,Science,USA 3,ABF,ENG,IND 3,ABF,MATHS,IND 3,ABF,Science,IND
读取代码:
import pandas as pd df1 = pd.read_clipboard(sep=',')
- df2 样例数据:
ID,Name,class,age 11,ABC,ENG,21 12,ABC,MATHS,23 1,ABC,Science,25 22,ABE,ENG,19 23,ABE,MATHS,22 24,ABE,Science,26 33,ABF,ENG,24 31,ABF,MATHS,28 32,ABF,Science,26
读取代码:
df2 = pd.read_clipboard(sep=',')
需求与约束
核心需求
- 校验df1中字段组合是否存在于df2中(从期望输出可判断实际匹配键为
ID+Name+Sub,df2中与Sub对应的字段为class) - 匹配成功的行在df1中新增
Status列标记为Yes,其余标记为No - 期望输出效果:
ID,Name,Sub,Country,Status 1,ABC,ENG,UK,No 1,ABC,MATHS,UK,No 1,ABC,Science,UK,Yes 2,ABE,ENG,USA,No 2,ABE,MATHS,USA,No 2,ABE,Science,USA,No 3,ABF,ENG,IND,No 3,ABF,MATHS,IND,No 3,ABF,Science,IND,No
约束条件
- df2为百万级数据,需保证执行效率
- 禁止使用
~取反运算符、not in操作符,避免生成无关结果降低性能 - 原有尝试代码仅能筛选匹配行,无法为df1全量行打标:
ID_list = df1['ID'].unique().tolist() Name_list = df1['Name'].unique().tolist() filtered_df = df2[((df2['ID'].isin(ID_list)) & (df2['Name'].isin(Name_list)))] filtered_df = filtered_df.groupby(['ID','Name','Sub']).size().reset_index()
高效实现方案
采用Pandas内置优化的左连接实现,全程无禁止操作符,百万级数据下性能优异,代码如下:
# 1. 预处理df2:仅提取匹配需要的键列,去重减少计算量,统一列名 match_df = df2[['ID', 'Name', 'class']].drop_duplicates().rename(columns={'class': 'Sub'}) # 新增匹配标记列 match_df['Status'] = 'Yes' # 2. 左连接合并到df1,保留df1全部行 df_result = df1.merge( match_df, on=['ID', 'Name', 'Sub'], how='left' ) # 3. 未匹配到的行填充为No,无需使用取反或not in df_result['Status'] = df_result['Status'].fillna('No')
方案说明
- 提前对df2的匹配键去重,大幅减少参与连接计算的数据量,降低内存占用
- 用Pandas底层C优化的
merge做哈希连接,性能比多条件isin、逐行遍历高2~10倍,适配百万级数据场景 - 仅用
fillna处理未匹配行,完全规避禁止的~、not in操作 - 如果实际仅需匹配
ID+Name两个字段,只需要调整merge的on参数为['ID', 'Name'],同时预处理match_df时仅保留这两列即可
内容的提问来源于stack exchange,提问作者The Great
相关产品推荐
相关产品推荐

