合并多个DataFrame后如何去除重复组合以缩减行数?
问题描述
使用reduce和pd.merge合并一组DataFrame,合并键为Name、Number、Country,但同一键组下的多值列会生成笛卡尔积,导致行数冗余(示例中Jack的组合从期望的2行变成8行),需要将每组重复项的行数精简到最少。
尝试的代码
merged = reduce(lambda left, right: pd.merge(left, right, on=['Name', 'Number', 'Country'], how="outer"), list_of_dfs)
当前输出(冗余结果)
| Name | Number | Country | Fruit | Sport | Street | School | Color | Grade |
|---|---|---|---|---|---|---|---|---|
| Jack | 23 | Canada | Banana | Basketball | Kiss | Middle | Blue | 91 |
| Jack | 23 | Canada | Banana | Basketball | Kiss | High | Blue | 91 |
| Jack | 23 | Canada | Banana | Basketball | Boss | Middle | Blue | 91 |
| Jack | 23 | Canada | Banana | Basketball | Boss | High | Blue | 91 |
| Jack | 23 | Canada | Banana | Soccer | Kiss | Middle | Blue | 91 |
| Jack | 23 | Canada | Banana | Soccer | Kiss | High | Blue | 91 |
| Jack | 23 | Canada | Banana | Soccer | Boss | Middle | Blue | 91 |
| Jack | 23 | Canada | Banana | Soccer | Boss | High | Blue | 91 |
输入的6个DataFrame示例
DataFrame 1
| Name | Number | Country | Fruit |
|---|---|---|---|
| Jack | 23 | Canada | Banana |
DataFrame 2
| Name | Number | Country | Sport |
|---|---|---|---|
| Jack | 23 | Canada | Basketball |
| Jack | 23 | Canada | Soccer |
DataFrame 3
| Name | Number | Country | Street |
|---|---|---|---|
| Jack | 23 | Canada | Kiss |
| Jack | 23 | Canada | Boss |
DataFrame 4
| Name | Number | Country | School |
|---|---|---|---|
| Jack | 23 | Canada | Middle |
| Jack | 23 | Canada | High |
DataFrame 5
| Name | Number | Country | Color |
|---|---|---|---|
| Jack | 23 | Canada | Blue |
DataFrame 6
| Name | Number | Country | Grade |
|---|---|---|---|
| Jack | 23 | Canada | 91 |
期望输出(精简结果)
| Name | Number | Country | Fruit | Sport | Street | School | Color | Grade |
|---|---|---|---|---|---|---|---|---|
| Jack | 23 | Canada | Banana | Basketball | Kiss | Middle | Blue | 91 |
| Jack | 23 | Canada | Banana | Soccer | Boss | High | Blue | 91 |
解决方案
核心思路是给每个键组(Name、Number、Country)内的行添加分组序号,合并时将序号也作为合并键,避免笛卡尔积,实现一对一匹配。
步骤1:给每个DataFrame添加分组内序号
定义函数为每个DataFrame的键组内生成连续序号:
import pandas as pd from functools import reduce def add_group_index(df): # 给每个键组内的行分配序号,从0开始 df['_group_idx'] = df.groupby(['Name', 'Number', 'Country']).cumcount() return df
步骤2:处理所有输入DataFrame
# 给所有DataFrame添加分组序号 list_of_dfs_with_idx = [add_group_index(df) for df in list_of_dfs]
步骤3:包含序号的合并
合并时同时使用原键和分组序号,确保同一序号的行匹配:
merged = reduce(lambda left, right: pd.merge(left, right, on=['Name', 'Number', 'Country', '_group_idx'], how='outer'), list_of_dfs_with_idx)
步骤4:清理临时序号列
# 删除临时添加的序号列 merged = merged.drop('_group_idx', axis=1)
原理说明
原合并方式仅使用三个键,当某个DataFrame的同一键组存在多行时,merge会生成笛卡尔积(行数为各键组内行数的乘积)。添加分组序号后,每个键组内的第n行只会和其他DataFrame中同键组的第n行合并,最终行数等于所有DataFrame中同一键组的最大行数(示例中为2行),完美匹配期望结果。
内容的提问来源于stack exchange,提问作者umi_zumii
相关产品推荐
相关产品推荐

