如何匹配含重复项的DataFrame对并保留未匹配项?
处理含重复项的DataFrame中(ID, Value)对的匹配与剩余实例保留
可以通过给重复的(ID, Value)对添加组内序号的方式实现精准匹配,再找出未匹配的实例。以下是两种可行方案:
方法一:组内编号+外连接筛选
这种方法通过给每个重复的匹配对添加唯一序号,实现一对一的精准匹配,再筛选出未匹配的行:
- 为两个DataFrame的每个
(ID, Value)组添加累计计数,用来区分同组内的重复实例 - 基于
ID、Value和计数列做外连接,保留所有行 - 筛选出仅在其中一个DataFrame中存在的行,即为未匹配的实例
import pandas as pd # 修正示例数据:字符串需加引号 df_1 = pd.DataFrame(data={'ID':['A', 'A', 'A', 'A','B','B','B','D'], 'Value':[1, 1, 1, 2, 1, 1, 2,3]}) df_2 = pd.DataFrame(data={'ID':['A','A','A','B','B','C'], 'Value':[1,1,2,1,2,4]}) # 给每个(ID, Value)组添加组内序号(从0开始计数) df_1['match_idx'] = df_1.groupby(['ID', 'Value']).cumcount() df_2['match_idx'] = df_2.groupby(['ID', 'Value']).cumcount() # 外连接,标记行的来源 merged = pd.merge(df_1, df_2, on=['ID', 'Value', 'match_idx'], how='outer', indicator=True) # 筛选出仅在df1或仅在df2中的行 remaining_rows = merged[merged['_merge'] != 'both'] # 整理成目标格式 remaining_df = remaining_rows[['ID', 'Value']].reset_index(drop=True) print(remaining_df)
运行结果与你期望的一致:
ID Value 0 A 1 1 B 1 2 B 2 3 C 4 4 D 3
方法二:基于统计差值生成剩余行(对应你的思路)
如果你想通过拼接字段、统计数量的方式实现,可以按以下步骤操作:
- 分别统计两个DataFrame中每个
(ID, Value)对的出现次数 - 合并统计结果,计算每个对的数量差值
- 根据差值生成对应的剩余行
# 统计每个(ID, Value)对的出现次数 counts_df1 = df_1.groupby(['ID', 'Value']).size().reset_index(name='count_df1') counts_df2 = df_2.groupby(['ID', 'Value']).size().reset_index(name='count_df2') # 合并统计结果,缺失的数量补0 total_counts = pd.merge(counts_df1, counts_df2, on=['ID', 'Value'], how='outer').fillna(0) # 计算每个对需要保留的实例数量:取两边数量的差值(仅保留正数) total_counts['remaining_num'] = total_counts.apply( lambda row: max(row['count_df1'] - row['count_df2'], 0) if row['count_df1'] > row['count_df2'] else max(row['count_df2'] - row['count_df1'], 0), axis=1 ) # 根据剩余数量生成重复行 remaining_df = total_counts[total_counts['remaining_num'] > 0].apply( lambda row: pd.DataFrame({'ID': [row['ID']]*int(row['remaining_num']), 'Value': [row['Value']]*int(row['remaining_num'])}), axis=1 ).concat().reset_index(drop=True) print(remaining_df)
这个方案的核心是先统计每个匹配对的数量差,再根据差值生成对应的剩余实例,完全贴合你最初的思路。
内容的提问来源于stack exchange,提问作者AstigX Tutorials
相关产品推荐
相关产品推荐

