基于多规则的两个Pandas DataFrame匹配方案咨询
Pandas DataFrame按优先级规则实现唯一匹配的高效方案
核心思路
不用循环,靠分层匹配+标记已用记录的方式实现:按匹配优先级从高到低依次匹配,每完成一级匹配就从数据库中移除已被使用的记录,确保每条数据库记录仅被匹配一次。
实现步骤与代码
假设我们有两个结构一致的DataFrame:df_persons(待匹配数据集)和df_database(候选匹配数据集),列均为cu_id, sex, eye_colour, favourite_sport, cash_on_account。
1. 预处理:准备临时数据库与匹配状态跟踪
import pandas as pd # 复制数据库到临时表,避免修改原数据 temp_db = df_database.copy() # 初始化匹配结果表 matched_results = pd.DataFrame(columns=df_persons.columns.tolist() + ['matched_cu_id']) # 标记未匹配的人员(初始为全部) unmatched_persons = df_persons.copy()
2. 按优先级依次匹配
优先级1:匹配sex、eye_colour、favourite_sport全一致的记录
# 合并匹配 level1_match = pd.merge( unmatched_persons, temp_db, on=['sex', 'eye_colour', 'favourite_sport'], suffixes=('_person', '_db') ) # 对每个人员取第一条匹配(保证唯一),并提取关键列 level1_match = level1_match.groupby('cu_id_person').first().reset_index() level1_match = level1_match[['cu_id_person', 'cu_id_db']].rename(columns={'cu_id_person': 'cu_id', 'cu_id_db': 'matched_cu_id'}) # 更新匹配结果、未匹配人员和临时数据库 matched_results = pd.concat([matched_results, level1_match], ignore_index=True) unmatched_persons = unmatched_persons[~unmatched_persons['cu_id'].isin(level1_match['cu_id'])] temp_db = temp_db[~temp_db['cu_id'].isin(level1_match['matched_cu_id'])]
优先级2:匹配sex、eye_colour一致的记录
if not unmatched_persons.empty and not temp_db.empty: level2_match = pd.merge( unmatched_persons, temp_db, on=['sex', 'eye_colour'], suffixes=('_person', '_db') ) level2_match = level2_match.groupby('cu_id_person').first().reset_index() level2_match = level2_match[['cu_id_person', 'cu_id_db']].rename(columns={'cu_id_person': 'cu_id', 'cu_id_db': 'matched_cu_id'}) matched_results = pd.concat([matched_results, level2_match], ignore_index=True) unmatched_persons = unmatched_persons[~unmatched_persons['cu_id'].isin(level2_match['cu_id'])] temp_db = temp_db[~temp_db['cu_id'].isin(level2_match['matched_cu_id'])]
优先级3:仅匹配sex一致的记录
if not unmatched_persons.empty and not temp_db.empty: level3_match = pd.merge( unmatched_persons, temp_db, on=['sex'], suffixes=('_person', '_db') ) level3_match = level3_match.groupby('cu_id_person').first().reset_index() level3_match = level3_match[['cu_id_person', 'cu_id_db']].rename(columns={'cu_id_person': 'cu_id', 'cu_id_db': 'matched_cu_id'}) matched_results = pd.concat([matched_results, level3_match], ignore_index=True) unmatched_persons = unmatched_persons[~unmatched_persons['cu_id'].isin(level3_match['cu_id'])] temp_db = temp_db[~temp_db['cu_id'].isin(level3_match['matched_cu_id'])]
优先级4:匹配cash_on_account最高的记录
if not unmatched_persons.empty and not temp_db.empty: # 先给数据库按cash_on_account降序排序,确保优先取金额最高的 temp_db_sorted = temp_db.sort_values('cash_on_account', ascending=False).reset_index(drop=True) # 给未匹配人员和排序后的数据库按索引匹配(一一对应,直到其中一方耗尽) match_count = min(len(unmatched_persons), len(temp_db_sorted)) level4_match = pd.DataFrame({ 'cu_id': unmatched_persons.iloc[:match_count]['cu_id'].values, 'matched_cu_id': temp_db_sorted.iloc[:match_count]['cu_id'].values }) # 处理剩余未匹配项 if len(unmatched_persons) > match_count: unmatched_persons = unmatched_persons.iloc[match_count:] temp_db = temp_db_sorted.iloc[match_count:] matched_results = pd.concat([matched_results, level4_match], ignore_index=True)
3. 合并最终结果
最后把匹配结果和原人员表合并,得到完整的匹配数据:
final_result = pd.merge(df_persons, matched_results, on='cu_id', how='left')
关键说明
- 每一级匹配后都会从临时数据库中移除已使用的记录,严格保证唯一匹配;
- 所有操作均基于Pandas的向量式运算,避免了循环带来的性能问题;
- 优先级4中采用按金额降序后按索引匹配的方式,高效实现“取最高金额”的需求,同时保证记录唯一。
内容的提问来源于stack exchange,提问作者workah0lic
相关产品推荐
相关产品推荐

