Skip to content

Python Pandas - 合并/连接

在数据分析中,组合来自不同来源的数据是一项常见任务。Pandas 提供了强大且灵活的工具,用于合并(merging)和连接(joining) DataFrame 对象,提供了类似于关系型数据库(如 SQL)的高性能内存操作。

基于共同列或索引组合 DataFrame 的主要函数是 pd.merge()。其基本签名如下:

pd.merge(left, right, how='inner', on=None, left_on=None, right_on=None,
left_index=False, right_index=False, sort=True,
suffixes=('_x', '_y'), ...)

主要参数说明:

  • left: 第一个 DataFrame 对象。
  • right: 第二个 DataFrame 对象。
  • how: 指定要执行的合并类型。选项包括:'inner'(默认)、'outer'、'left'、'right'。详见下文。
  • on: 用于连接的列名。这些列必须存在于 两个 DataFrame 中。如果为 None 且未指定 left_on/right_on,则在列的交集上进行合并。
  • left_on: 用作连接键的 左侧 DataFrame 中的列名。
  • right_on: 用作连接键的 右侧 DataFrame 中的列名。当连接键在两个 DataFrame 中名称不同时,使用 left_on/right_on。
  • left_index: 如果为 True,使用 左侧 DataFrame 的索引作为连接键。
  • right_index: 如果为 True,使用 右侧 DataFrame 的索引作为连接键。
  • sort: 如果为 True(默认),则在结果中按字典顺序对连接键进行排序。设置为 False 可以显著提高性能。
  • suffixes: 用于分别添加到左侧和右侧 DataFrame 中重叠列名(非 连接键)的字符串元组(tuple)。默认为 ('_x', '_y')。

让我们创建两个示例 DataFrame 来演示合并:

# 导入 pandas 库
import pandas as pd
left = pd.DataFrame({
'id': [1, 2, 3, 4, 5],
'Student_Name': ['Alex', 'Amy', 'Allen', 'Alice', 'Ayoung'],
'subject_id': ['sub1', 'sub2', 'sub4', 'sub6', 'sub5']
})
right = pd.DataFrame({
'id': [1, 2, 3, 4, 5],
'Teacher_Name': ['Billy', 'Brian', 'Bran', 'Bryce', 'Betty'],
'subject_id': ['sub2', 'sub4', 'sub3', 'sub6', 'sub5']
})
print("左侧 DataFrame:")
print(left)
print("\n右侧 DataFrame:")
print(right)

输出:

左侧 DataFrame:
id Student_Name subject_id
0 1 Alex sub1
1 2 Amy sub2
2 3 Allen sub4
3 4 Alice sub6
4 5 Ayoung sub5
右侧 DataFrame:
id Teacher_Name subject_id
0 1 Billy sub2
1 2 Brian sub4
2 3 Bran sub3
3 4 Bryce sub6
4 5 Betty sub5

如果两个 DataFrame 共享一个名称相同并用作键的列,请使用 on 参数。

import pandas as pd
# (使用上面定义的 left 和 right DataFrame)
# 基于 'id' 列(两个表中都存在)进行合并
merged_on_id = pd.merge(left, right, on='id')
print("基于 'id' 合并的 DataFrame:")
print(merged_on_id)

输出:

基于 'id' 合并的 DataFrame:
id Student_Name subject_id_x Teacher_Name subject_id_y
0 1 Alex sub1 Billy sub2
1 2 Amy sub2 Brian sub4
2 3 Allen sub4 Bran sub3
3 4 Alice sub6 Bryce sub6
4 5 Ayoung sub5 Betty sub5

注意,subject_id 列在两个原始 DataFrame 中都存在,但不是连接键,它会自动获得后缀 _x(来自左侧)和 _y(来自右侧)。

基于多个键列进行合并 (on 与列表)

Section titled “基于多个键列进行合并 (on 与列表)”

要基于多个列的组合进行合并,请将列名列表(list)传递给 on。

import pandas as pd
# (使用上面定义的 left 和 right DataFrame)
# 仅在 'id' 和 'subject_id' 都匹配时进行合并
merged_on_multiple = pd.merge(left, right, on=['id', 'subject_id'])
print("基于 'id' 和 'subject_id' 合并的 DataFrame:")
print(merged_on_multiple)

输出:

基于 'id' 和 'subject_id' 合并的 DataFrame:
id Student_Name subject_id Teacher_Name
0 4 Alice sub6 Bryce
1 5 Ayoung sub5 Betty

仅保留 left 和 right 中 id 和 subject_id 都相同的行(id=4, subject_id=‘sub6’ 和 id=5, subject_id=‘sub5’)。

how 参数控制结果中包含哪些键,以及当键存在于一个 DataFrame 但不存在于另一个 DataFrame 时如何处理缺失值。

合并方法 (how)SQL 对等操作描述
'inner'(默认)INNER JOIN仅保留在左侧和右侧 DataFrame 中 都 找到的键。键的交集。
'left'LEFT OUTER JOIN保留左侧 DataFrame 中的 所有 键。如果某个键在右侧 DataFrame 中缺失,则将对应列填充 NaN。
'right'RIGHT OUTER JOIN保留右侧 DataFrame 中的 所有 键。如果某个键在左侧 DataFrame 中缺失,则将对应列填充 NaN。
'outer'FULL OUTER JOIN保留在左侧或右侧 DataFrame 中 任一 找到的 所有 键。对于在一个 DataFrame 中缺失的键,用 NaN 填充对应位置。键的并集。

让我们看看使用 subject_id 作为连接键的示例,该列在 left 和 right 中有不同的值。

import pandas as pd
# (使用上面定义的 left 和 right DataFrame)
# 保留来自 'left' 的所有 subject_id,与来自 'right' 的匹配
left_join = pd.merge(left, right, on='subject_id', how='left')
print("基于 'subject_id' 的左连接:")
print(left_join)

输出:

基于 'subject_id' 的左连接:
id_x Student_Name subject_id id_y Teacher_Name
0 1 Alex sub1 NaN NaN # sub1 只存在于 left
1 2 Amy sub2 1.0 Billy
2 3 Allen sub4 2.0 Brian
3 4 Alice sub6 4.0 Bryce
4 5 Ayoung sub5 5.0 Betty

来自 left 的所有 subject_id 值(sub1、sub2、sub4、sub6、sub5)都存在。由于 sub1 不存在于 right 中,因此该行来自 right 的列(id_y、Teacher_Name)为 NaN。

import pandas as pd
# (使用上面定义的 left 和 right DataFrame)
# 保留来自 'right' 的所有 subject_id,与来自 'left' 的匹配
right_join = pd.merge(left, right, on='subject_id', how='right')
print("基于 'subject_id' 的右连接:")
print(right_join)

输出:

基于 'subject_id' 的右连接:
id_x Student_Name subject_id id_y Teacher_Name
0 2.0 Amy sub2 1 Billy
1 3.0 Allen sub4 2 Brian
2 NaN NaN sub3 3 Bran # sub3 只存在于 right
3 4.0 Alice sub6 4 Bryce
4 5.0 Ayoung sub5 5 Betty

来自 right 的所有 subject_id 值(sub2、sub4、sub3、sub6、sub5)都存在。由于 sub3 不存在于 left 中,因此该行来自 left 的列(id_x、Student_Name)为 NaN。

import pandas as pd
# (使用上面定义的 left 和 right DataFrame)
# 保留来自 'left' 和 'right' 的所有 subject_id
outer_join = pd.merge(left, right, on='subject_id', how='outer')
print("基于 'subject_id' 的外连接:")
print(outer_join)

输出:

基于 'subject_id' 的外连接:
id_x Student_Name subject_id id_y Teacher_Name
0 1.0 Alex sub1 NaN NaN # 只在 left 中
1 2.0 Amy sub2 1.0 Billy
2 3.0 Allen sub4 2.0 Brian
3 4.0 Alice sub6 4.0 Bryce
4 5.0 Ayoung sub5 5.0 Betty
5 NaN NaN sub3 3.0 Bran # 只在 right 中

来自两个 DataFrame 的所有唯一 subject_id 值(sub1、sub2、sub3、sub4、sub5、sub6)都包含在内。当一个 subject ID 存在于一个 DataFrame 但不存在于另一个 DataFrame 时,对应位置会出现 NaN 值。

默认行为(how='inner')仅保留在 两个 DataFrame 中都存在的键。

import pandas as pd
# (使用上面定义的 left 和 right DataFrame)
# 仅保留 'left' 和 'right' 中都存在的 subject_id
inner_join = pd.merge(left, right, on='subject_id', how='inner')
# 等效于: inner_join = pd.merge(left, right, on='subject_id')
print("基于 'subject_id' 的内连接:")
print(inner_join)

输出:

基于 'subject_id' 的内连接:
id_x Student_Name subject_id id_y Teacher_Name
0 2 Amy sub2 1 Billy
1 3 Allen sub4 2 Brian
2 4 Alice sub6 4 Bryce
3 5 Ayoung sub5 5 Betty

仅出现 subject_id 值 sub2、sub4、sub5、sub6,因为这些是仅在 left 和 right DataFrame 中都找到的值。

除了 pd.merge() 外,Pandas 还提供了 DataFrame.join() 方法,它方便用于基于 DataFrame 索引进行连接,或将一个 DataFrame 的索引与另一个 DataFrame 的列进行连接。pd.concat() 是另一个相关函数,用于垂直或水平堆叠 DataFrame。然而,pd.merge() 是基于共同值组合 DataFrame 的最通用且类似 SQL 的函数。