Pandas多源数据整合之三大数据合并方法(Merge,Join与Concat )详解

 更新时间:2026年05月27日 09:13:13   作者:小庄-Python办公  
本文介绍了Pandas中三种数据整合方法,即merge、join和concat,重点讲解了merge函数的四种连接方式(内连接、左连接、右连接、外连接)及其参数使用,希望对大家有所帮助

本节学习目标

  • 理解数据库连接的各种类型(inner、outer、left、right)
  • 掌握 merge 函数的全部核心参数
  • 学会使用 join 进行基于索引的关联
  • 掌握 concat 进行轴向合并
  • 能够处理多表关联和数据对齐问题

为什么学这个?

在真实工作环境中,数据很少只存在于一张表中。以电商系统为例:

  • 用户表:存储用户的姓名、邮箱、注册日期
  • 订单表:存储订单号、用户 ID、下单时间、总金额
  • 产品表:存储产品 ID、名称、分类、单价

当你想回答"2024年购买电子产品最多的用户是谁"这个问题时,你需要同时关联这三张表

这正是本节的重点:多源数据整合

Pandas 提供了三种主要的数据合并方式:

  • merge —— 基于列值的关联(类似 SQL 的 JOIN)
  • join —— 基于索引的关联
  • concat —— 按轴方向拼接(上下或左右)

打个比方:如果说 groupby 是把数据"拆分再汇总",那么 merge/join/concat 就是把散落各处的数据"拼起来"。前者是拆解分析,后者是整合重组,两者同等重要。

核心知识点讲解

merge:基于列值的关联

merge 是 Pandas 中最常用的数据关联方法,它与 SQL 数据库中的 JOIN 操作非常类似。

1. 准备示例数据

import pandas as pd
import numpy as np

# 订单表
orders = pd.DataFrame({
    '订单ID': [1001, 1002, 1003, 1004, 1005],
    '用户ID': [1, 2, 3, 1, 4],
    '金额': [500, 300, 800, 200, 1500],
    '日期': ['2024-01-15', '2024-02-20', '2024-03-10', '2024-04-05', '2024-05-18']
})

# 用户表
users = pd.DataFrame({
    '用户ID': [1, 2, 3, 5],
    '姓名': ['张三', '李四', '王五', '赵六'],
    '城市': ['北京', '上海', '广州', '深圳']
})

# 产品表
products = pd.DataFrame({
    '订单ID': [1001, 1002, 1003, 1004, 1005],
    '产品名': ['手机', '耳机', '电脑', '充电器', '手表'],
    '品类': ['电子产品', '配件', '电子产品', '配件', '饰品']
})

print("订单表:")
print(orders)
print("\n用户表:")
print(users)
print("\n产品表:")
print(products)

2. 内连接(inner join)

内连接只保留两个表中都有的匹配行。

# 内连接:只保留有用户信息的订单
result = pd.merge(orders, users, on='用户ID', how='inner')
print("内连接(只保留有用户的订单):")
print(result)

# 注意:用户ID=4 的订单被丢弃了(因为用户表中没有ID=4的用户)
# 用户ID=5 的赵六也被丢弃了(因为订单表中没有他的订单)

Venn 图理解:inner join 就像两个圆圈的交集部分

3. 左连接(left join)

左连接保留左表的所有行,右表中没有匹配的填充 NaN。

# 左连接:保留所有订单,匹配不到的用户信息为 NaN
result = pd.merge(orders, users, on='用户ID', how='left')
print("左连接(保留所有订单):")
print(result)

# 订单ID=1005 的订单保留了,但用户信息(姓名、城市)为 NaN
# 因为用户表中没有用户ID=4的记录

左连接是最常用的连接方式,因为它保证了主表(左表)的数据完整性。

4. 右连接(right join)

右连接保留右表的所有行,左表中没有匹配的填充 NaN。

# 右连接:保留所有用户,没有订单的用户订单信息为 NaN
result = pd.merge(orders, users, on='用户ID', how='right')
print("右连接(保留所有用户):")
print(result)

# 赵六(用户ID=5)保留了,但订单信息为 NaN
# 因为他没有下过订单

5. 外连接(outer join)

外连接保留两个表的所有行,没有匹配的填充 NaN。

# 外连接:保留所有订单和所有用户
result = pd.merge(orders, users, on='用户ID', how='outer')
print("外连接(保留所有):")
print(result)

6. merge 参数详解

# ===== on / left_on / right_on =====

# on=:两个表的关联列名相同
result = pd.merge(orders, users, on='用户ID')

# left_on / right_on:关联列名不同
users2 = pd.DataFrame({
    'ID': [1, 2, 3, 5],  # 列名不同
    '姓名': ['张三', '李四', '王五', '赵六'],
    '城市': ['北京', '上海', '广州', '深圳']
})

result = pd.merge(
    orders, users2,
    left_on='用户ID',    # 左表的关联列
    right_on='ID'        # 右表的关联列
)
print("不同列名关联:")
print(result)

# ===== suffixes:处理重名列 =====
orders2 = pd.DataFrame({
    '订单ID': [1001, 1002],
    '金额': [500, 300],
    '更新时间': ['2024-01-15', '2024-02-20']  # 与右表有重名列
})

users3 = pd.DataFrame({
    '订单ID': [1001, 1002],
    '金额': [550, 320],  # 重名列
    '更新时间': ['2024-01-16', '2024-02-21']  # 重名列
})

result = pd.merge(
    orders2, users3,
    on='订单ID',
    how='left',
    suffixes=('_订单', '_用户')  # 自定义后缀
)
print("重名列处理:")
print(result)

# ===== indicator:显示匹配来源 =====
result = pd.merge(orders, users, on='用户ID', how='outer', indicator=True)
print("匹配来源:")
print(result)
# _merge 列会显示 "both"、"left_only"、"right_only"

7. 多表关联

# 三表关联:先关联 orders + users,再关联 products
result = pd.merge(orders, users, on='用户ID', how='left')
result = pd.merge(result, products, on='订单ID', how='left')
print("三表关联结果:")
print(result)

# 链式写法
result = (orders
          .merge(users, on='用户ID', how='left')
          .merge(products, on='订单ID', how='left'))
print("\n链式写法:")
print(result)

join:基于索引的关联

join 是基于索引进行关联的方法。它本质上是 merge 的便捷版本,当你的关联键是索引时,使用 join 更简洁。

# 设置索引
orders_idx = orders.set_index('用户ID')
users_idx = users.set_index('用户ID')

print("orders 索引:")
print(orders_idx)
print("\nusers 索引:")
print(users_idx)

# ===== 左连接(join 默认就是左连接)=====
result = orders_idx.join(users_idx, lsuffix='_订单', rsuffix='_用户')
print("join 左连接:")
print(result)

# ===== 指定连接方式 =====
result = orders_idx.join(users_idx, how='inner', lsuffix='_订单', rsuffix='_用户')
print("\njoin 内连接:")
print(result)

# join 的等价 merge 写法:
# result = pd.merge(orders, users, left_index=True, right_index=True, how='left')

concat:轴向拼接

concat 用于将多个 DataFrame 按轴方向拼接——可以是上下拼接(纵向),也可以是左右拼接(横向)。

1. 纵向拼接(axis=0,默认)

# 两个结构相同的 DataFrame
df1 = pd.DataFrame({
    '姓名': ['张三', '李四'],
    '年龄': [25, 30],
    '城市': ['北京', '上海']
})

df2 = pd.DataFrame({
    '姓名': ['王五', '赵六'],
    '年龄': [28, 35],
    '城市': ['广州', '深圳']
})

# 上下拼接
result = pd.concat([df1, df2])
print("上下拼接:")
print(result)

# 忽略原索引
result = pd.concat([df1, df2], ignore_index=True)
print("\n忽略原索引:")
print(result)

# 添加来源标记
result = pd.concat([df1, df2], keys=['表1', '表2'])
print("\n带来源标记:")
print(result)

2. 横向拼接(axis=1)

# 两个结构不同的 DataFrame
df_a = pd.DataFrame({
    'A': [1, 2, 3],
    'B': [4, 5, 6]
})

df_b = pd.DataFrame({
    'C': [7, 8, 9],
    'D': [10, 11, 12]
})

# 左右拼接
result = pd.concat([df_a, df_b], axis=1)
print("左右拼接:")
print(result)

3. concat 的数据对齐行为

# concat 会按索引对齐
df_x = pd.DataFrame({'A': [1, 2]}, index=[0, 1])
df_y = pd.DataFrame({'B': [3, 4]}, index=[1, 2])

result = pd.concat([df_x, df_y], axis=1)
print("索引对齐拼接:")
print(result)
# 输出:
#      A    B
# 0  1.0  NaN
# 1  2.0  3.0
# 2  NaN  4.0

# inner:只保留共有的索引
result = pd.concat([df_x, df_y], axis=1, join='inner')
print("\ninner 对齐:")
print(result)
# 只保留索引 1

对比与选择指南

方法适用场景关联依据示例
merge表关联(类似 SQL JOIN)列值merge(df1, df2, on='id')
join基于索引的关联索引df1.join(df2)
concat数据追加或并排拼接轴方向concat([df1, df2])

快速决策

  • 有共同的列名做关联?→ 用 merge
  • 索引就是关联键?→ 用 join(或 merge(left_index=True, right_index=True)
  • 只是把两个表上下或左右拼起来?→ 用 concat

数据对齐与常见问题

1. 重复列名处理

# 关联后可能出现重复列
df1 = pd.DataFrame({'ID': [1, 2], 'name': ['A', 'B'], 'score': [90, 85]})
df2 = pd.DataFrame({'ID': [1, 2], 'name': ['A', 'B'], 'grade': ['A', 'B']})

result = pd.merge(df1, df2, on='ID', suffixes=('_1', '_2'))
print(result)

2. 一对多关联

# 一个用户对应多个订单(一对多)
users = pd.DataFrame({
    '用户ID': [1, 2, 3],
    '姓名': ['张三', '李四', '王五']
})

orders = pd.DataFrame({
    '订单ID': [101, 102, 103, 104],
    '用户ID': [1, 1, 2, 3],  # 用户1有两个订单
    '金额': [500, 300, 800, 200]
})

result = pd.merge(users, orders, on='用户ID', how='left')
print("一对多关联:")
print(result)
# 张三会出现两行,分别对应两个订单

3. 多对多关联

# 多对多关联会产生笛卡尔积
df1 = pd.DataFrame({'key': ['A', 'A', 'B'], 'val1': [1, 2, 3]})
df2 = pd.DataFrame({'key': ['A', 'A', 'B'], 'val2': [4, 5, 6]})

result = pd.merge(df1, df2, on='key')
print("多对多关联(笛卡尔积):")
print(result)
# A-A 组合产生 2×2=4 行
# 注意:Pandas 3.x 中多对多关联需要显式指定 validate 参数

实战练习

练习 1:三表关联查询

题目:关联学生、课程、成绩三张表。

# 参考答案
import pandas as pd

students = pd.DataFrame({
    '学号': [101, 102, 103, 104],
    '姓名': ['小明', '小红', '小刚', '小丽'],
    '班级': ['一班', '二班', '一班', '三班']
})

courses = pd.DataFrame({
    '课程号': ['C01', 'C02', 'C03'],
    '课程名': ['语文', '数学', '英语'],
    '学分': [4, 5, 4]
})

grades = pd.DataFrame({
    '学号': [101, 101, 102, 102, 103, 103, 104, 104],
    '课程号': ['C01', 'C02', 'C01', 'C03', 'C02', 'C03', 'C01', 'C02'],
    '成绩': [85, 90, 92, 88, 78, 82, 96, 85]
})

# 1. 关联学生 + 成绩
result = students.merge(grades, on='学号', how='left')
result = result.merge(courses, on='课程号', how='left')
print("学生-课程-成绩关联:")
print(result)

# 2. 计算每个学生的平均分
avg = result.groupby(['学号', '姓名'])['成绩'].mean().reset_index()
avg.columns = ['学号', '姓名', '平均分']
print("\n学生平均分:")
print(avg.round(1))

练习 2:数据拼接实践

题目:将多个季度的数据文件合并。

# 参考答案
import pandas as pd
import numpy as np

# 模拟四个季度的数据
q1 = pd.DataFrame({
    '季度': ['Q1'] * 3,
    '产品': ['A', 'B', 'C'],
    '销售额': [1000, 2000, 1500]
})

q2 = pd.DataFrame({
    '季度': ['Q2'] * 3,
    '产品': ['A', 'B', 'C'],
    '销售额': [1200, 2200, 1600]
})

q3 = pd.DataFrame({
    '季度': ['Q3'] * 3,
    '产品': ['A', 'B', 'C'],
    '销售额': [1100, 2100, 1700]
})

q4 = pd.DataFrame({
    '季度': ['Q4'] * 3,
    '产品': ['A', 'B', 'C'],
    '销售额': [1300, 2300, 1800]
})

# 纵向拼接
df = pd.concat([q1, q2, q3, q4], ignore_index=True)
print("年度合并数据:")
print(df)

# 计算年度汇总
summary = df.groupby('产品')['销售额'].agg(['sum', 'mean']).reset_index()
summary.columns = ['产品', '年销售额', '季均销售额']
print("\n年度汇总:")
print(summary)

本节总结

本节我们学习了 Pandas 的三种数据整合方法:

merge —— 基于列值的表关联:

  • how 参数:'inner'(交集)、'left'(保留左表)、'right'(保留右表)、'outer'(并集)
  • on / left_on / right_on 指定关联列
  • suffixes 处理重名列

join —— 基于索引的关联:

  • 默认左连接,简洁方便
  • 适合关联键为索引的场景

concat —— 轴向拼接:

  • axis=0(默认):上下拼接
  • axis=1:左右拼接
  • keys 添加来源标记
  • 按索引自动对齐

到此这篇关于Pandas多源数据整合之三大数据合并方法(Merge,Join与Concat )详解的文章就介绍到这了,更多相关Pandas数据合并内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • python避免死锁方法实例分析

    python避免死锁方法实例分析

    这篇文章主要介绍了python避免死锁方法,较为详细的分析了死锁的成因与避免形成死锁的方法,需要的朋友可以参考下
    2015-06-06
  • 利用Python求阴影部分的面积实例代码

    利用Python求阴影部分的面积实例代码

    这篇文章主要给大家介绍了关于利用Python求阴影部分面积的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用python具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-12-12
  • pytorch如何自定义数据集

    pytorch如何自定义数据集

    这篇文章主要介绍了pytorch自定义数据集,在识别手写数字的例子中,数据集是直接下载的,但如果我们自己收集了一些数据,存在电脑文件夹里,我们该如何把这些数据变为可以在PyTorch框架下进行神经网络训练的数据集呢,即如何自定义数据集呢,需要的朋友可以参考下
    2024-01-01
  • 浅析NumPy 切片和索引

    浅析NumPy 切片和索引

    这篇文章主要介绍了NumPy 切片和索引的相关资料,帮助大家更好的理解和学习NumPy的相关知识,感兴趣的朋友可以了解下。
    2020-09-09
  • python 获取本机ip地址的两个方法

    python 获取本机ip地址的两个方法

    用python 获取本机ip地址的多种方法,需要的朋友可以参考下
    2013-02-02
  • Python中的if判断语句中包含or问题

    Python中的if判断语句中包含or问题

    这篇文章主要介绍了Python中的if判断语句中包含or问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • Python接口自动化浅析数据驱动原理

    Python接口自动化浅析数据驱动原理

    这篇文章主要介绍了Python接口自动化浅析数据驱动原理,文中会详细描述怎样使用openpyxl模块操作excel及结合ddt来实现数据驱动,有需要的朋友可以参考下
    2021-08-08
  • 使用Python高效实现MySQL数据同步的几种方案

    使用Python高效实现MySQL数据同步的几种方案

    在数据驱动的现代应用中,数据库同步是确保数据一致性和可用性的关键环节,MySQL作为最流行的开源关系型数据库之一,其数据同步需求广泛存在于主从复制、数据迁移、备份恢复等场景,本文将详细介绍如何使用Python实现高效可靠的MySQL数据同步方案,需要的朋友可以参考下
    2025-10-10
  • Python标准库中隐藏的利器(示例详解)

    Python标准库中隐藏的利器(示例详解)

    在命令行中直接使用Python标准库的模块,最大的好处就是就是不用写代码,就能使用其中的功能,当临时需要一些某些功能的时候,用这种方式会快捷,方便很多,这篇文章主要介绍了Python标准库中隐藏的利器,需要的朋友可以参考下
    2023-11-11
  • django 使用 PIL 压缩图片的例子

    django 使用 PIL 压缩图片的例子

    今天小编就为大家分享一篇django 使用 PIL 压缩图片的例子,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-08-08

最新评论