查看全课大纲(第 60 / 101 节)
  1. 1.数据分析基本概念
  2. 2.学习数据分析的一般路线
  3. 3.数据分析的流程
  4. 4.数据类型
  5. 5.环境部署(1)
  6. 6.环境部署(2)
  7. 7.课程介绍
  8. 8.TXT文件操作
  9. 9.JSON文件操作
  10. 10.CSV文件操作
  11. 11.Excel文件操作
  12. 12.数据库及SQL常用语法
  13. 13.数据库基本操作
  14. 14.数据库多表连接
  15. 15.实战:欧洲职业足球数据库分析
  16. 16.爬虫简介
  17. 17.URL管理模块
  18. 18.网页下载模块
  19. 19.网页解析模块(1)
  20. 20.网页解析模块(2)
  21. 21.Scrapy简介
  22. 22.Scrapy使用步骤(1)
  23. 23.Scrapy使用步骤(2)
  24. 24.Scrapy使用步骤(3)
  25. 25.Scrapy使用步骤(4)
  26. 26.实战:获取国内城市空气质量指数数据
  27. 27.NumPy和SciPy介绍
  28. 28.多维数组
  29. 29.多维数组操作
  30. 30.NumPy的常用方法
  31. 31.向量化介绍
  32. 32.向量化及通用函数
  33. 33.实战:2016美国大选分析
  34. 34.数据结构-Series
  35. 35.数据结构-DataFrame
  36. 36.数据结构-Index
  37. 37.Series的索引操作
  38. 38.DataFrame的索引操作
  39. 39.索引操作总结
  40. 40.运算与对齐
  41. 41.函数应用操作(1) -- map
  42. 42.函数应用操作 (2) -- apply applymap
  43. 43.文件读写操作
  44. 44.排序操作
  45. 45.数据清洗--处理缺失数据
  46. 46.数据清洗--处理重复数据
  47. 47.数据清洗--替换数据
  48. 48.常用统计方法(1) -- describe quantile
  49. 49.常用统计方法(2) -- sum mean median count
  50. 50.常用统计方法(3) -- max min idxmax idxmin
  51. 51.常用统计方法(4) -- mad var std cumsum
  52. 52.实战:全球食品数据分析
  53. 53.层级索引
  54. 54.分组与聚合介绍
  55. 55.分组操作(1) -- GroupBy对象及常用聚合操作
  56. 56.分组操作(2) -- 自定义分组及聚合操作
  57. 57.透视表介绍
  58. 58.透视表操作
  59. 59.数据规整(1) -- 数据合并concat
  60. 60.数据规整(2) -- 数据连接merge
  61. 61.数据重构(3) -- 数据重构stack unstack
  62. 62.实战:互联网电影资料库分析
  63. 63.探索性数据分析EDA介绍
  64. 64.EDA的目的
  65. 65.EDA常用工具
  66. 66.Matplotlib绘图基本介绍
  67. 67.Matplotlib画布
  68. 68.散点图和柱状图的绘制
  69. 69.直方图的绘制
  70. 70.矩阵绘图
  71. 71.子图的使用
  72. 72.Matplotlib颜色、标记、线型
  73. 73.Matplotlib坐标刻度、标签、图例、标题
  74. 74.Seaborn介绍
  75. 75.数据集分布可视化(1) -- 单变量分布、双变量分布
  76. 76.数据集分布可视化(2) -- 变量关系可视化
  77. 77.类别数据可视化 -- 类别散布图、类别内数据分布、类别内统计图
  78. 78.交互式数据可视化工具Bokeh介绍
  79. 79.Bokeh绘制散点图、柱状图、盒子图、弦图
  80. 80.Bokeh绘制常用图形元素
  81. 81.3D绘图 -- mplot3d
  82. 82.3D曲线可视化
  83. 83.3D散点图可视化
  84. 84.3D柱状图可视化
  85. 85.Pandas绘图
  86. 86.实战:Lending Club借贷数据探索性分析及可视化
  87. 87.机器学习介绍及应用场景
  88. 88.机器学习建模介绍 (1) -- 分类
  89. 89.机器学习建模介绍 (2) -- 回归
  90. 90.机器学习建模介绍 (3) -- 聚类
  91. 91.机器学习分类
  92. 92.机器学习工具scikit-learn
  93. 93.使用scikit-learn的流程
  94. 94.数据集准备及划分
  95. 95.模型选择
  96. 96.数据预处理及特征工程
  97. 97.过拟合与欠拟合
  98. 98.模型调参介绍
  99. 99.模型调参方法
  100. 100.模型测试及评价
  101. 101.实战:通过移动设备行为数据预测性别和年龄

数据规整(2) -- 数据连接merge

约 14 分钟

📺 正在播放小象学院官方高清录播(支持倍速与清晰度调节)

数据连接 merge

小象实战讲义 · Python数据分析实战

在数据分析工作中,数据往往分散在多个表格或数据源中。例如,用户信息在一个表格,购买记录在另一个表格,产品信息又在第三个表格。要将这些信息整合起来进行分析,就需要用到数据连接操作。本节将学习 Pandas 中强大的 merge() 函数,它能够像数据库的 JOIN 操作一样,根据共同的列将不同的 DataFrame 连接起来。

💡 核心导读

  • 数据连接的概念:理解 merge() 与之前学过的 concat() 的区别,掌握基于“键”(列)的连接思想。
  • 四种连接方式:学习外连接、内连接、左连接和右连接的区别与应用场景。
  • 灵活指定连接键:掌握当列名不同或需要按索引连接时的参数设置。
  • 处理重复列名:了解连接后重复列名的自动处理机制与自定义后缀方法。

1. merge() 的基本概念

merge() 是 Pandas 中用于数据连接的核心函数。它与之前学习的 concat() 有本质区别:

  • concat():是简单的拼接,将数据沿某个轴(横向或纵向)堆叠在一起,不关心内容关联。
  • merge():是基于一个或多个“键”(通常是列名)将不同 DataFrame 的行关联起来,类似于数据库的表连接(JOIN)操作。

为什么需要 merge? 想象一个实际场景:一个表格记录员工信息(姓名、部门),另一个表格记录员工技能(姓名、技能)。要分析每个部门员工的技能分布,就需要根据“姓名”这个共同的列将两个表格连接起来。

merge() 默认会将两个 DataFrame 中列名相同的列作为连接键。你也可以通过参数显式指定。

import pandas as pd

# 创建两个示例 DataFrame
staff_df = pd.DataFrame([{'姓名': '张三', '部门': '研发部'},
                        {'姓名': '李四', '部门': '财务部'},
                        {'姓名': '赵六', '部门': '市场部'}])

student_df = pd.DataFrame([{'姓名': '张三', '专业': '计算机'},
                          {'姓名': '李四', '专业': '会计'},
                          {'姓名': '王五', '专业': '市场营销'}])

print("员工信息表:")
print(staff_df)
print("\n学生信息表:")
print(student_df)
员工信息表:
   姓名   部门
0  张三  研发部
1  李四  财务部
2  赵六  市场部

学生信息表:
   姓名    专业
0  张三   计算机
1  李四    会计
2  王五  市场营销

观察这两个表格:

  • 张三和李四同时出现在两个表中
  • 赵六只在员工表中(可能不是学生,或是实习生但未登记专业)
  • 王五只在学生表中(可能未在该公司实习)

2. 四种连接方式详解

merge() 通过 how 参数指定连接方式,这与数据库的 JOIN 类型完全对应。

2.1 外连接 (outer)

外连接取两个 DataFrame 的并集。只要数据出现在任一个表中,就会出现在结果中,缺失的部分用 NaN(空值)填充。

import pandas as pd

# 重新创建数据(确保每个代码块自包含)
staff_df = pd.DataFrame([{'姓名': '张三', '部门': '研发部'},
                        {'姓名': '李四', '部门': '财务部'},
                        {'姓名': '赵六', '部门': '市场部'}])

student_df = pd.DataFrame([{'姓名': '张三', '专业': '计算机'},
                          {'姓名': '李四', '专业': '会计'},
                          {'姓名': '王五', '专业': '市场营销'}])

# 外连接
result_outer = pd.merge(staff_df, student_df, how='outer', on='姓名')
print("外连接结果(并集):")
print(result_outer)
外连接结果(并集):
   姓名   部门    专业
0  张三  研发部   计算机
1  李四  财务部    会计
2  王五  NaN  市场营销
3  赵六  市场部   NaN

结果解读:

  • 张三、李四:两个表都有,信息完整
  • 赵六:只有员工信息,专业为空
  • 王五:只有学生信息,部门为空

2.2 内连接 (inner)

内连接取两个 DataFrame 的交集。只保留在两个表中都出现的记录。

import pandas as pd

staff_df = pd.DataFrame([{'姓名': '张三', '部门': '研发部'},
                        {'姓名': '李四', '部门': '财务部'},
                        {'姓名': '赵六', '部门': '市场部'}])

student_df = pd.DataFrame([{'姓名': '张三', '专业': '计算机'},
                          {'姓名': '李四', '专业': '会计'},
                          {'姓名': '王五', '专业': '市场营销'}])

# 内连接
result_inner = pd.merge(staff_df, student_df, how='inner', on='姓名')
print("内连接结果(交集):")
print(result_inner)
内连接结果(交集):
   姓名   部门   专业
0  张三  研发部  计算机
1  李四  财务部   会计

结果解读:只保留了张三和李四,因为只有他们同时出现在两个表中。

2.3 左连接 (left)

左连接以左边的 DataFrame 为基准,保留左边的所有记录。右边表中没有匹配的记录,对应字段填 NaN。

import pandas as pd

staff_df = pd.DataFrame([{'姓名': '张三', '部门': '研发部'},
                        {'姓名': '李四', '部门': '财务部'},
                        {'姓名': '赵六', '部门': '市场部'}])

student_df = pd.DataFrame([{'姓名': '张三', '专业': '计算机'},
                          {'姓名': '李四', '专业': '会计'},
                          {'姓名': '王五', '专业': '市场营销'}])

# 左连接(以staff_df为基准)
result_left = pd.merge(staff_df, student_df, how='left', on='姓名')
print("左连接结果(保留左边所有记录):")
print(result_left)
左连接结果(保留左边所有记录):
   姓名   部门   专业
0  张三  研发部  计算机
1  李四  财务部   会计
2  赵六  市场部  NaN

结果解读:

  • 员工表的三条记录全部保留
  • 赵六没有匹配的专业信息,所以专业列为空
  • 学生表中的王五(不在员工表中)不出现

2.4 右连接 (right)

右连接以右边的 DataFrame 为基准,与左连接正好相反。

import pandas as pd

staff_df = pd.DataFrame([{'姓名': '张三', '部门': '研发部'},
                        {'姓名': '李四', '部门': '财务部'},
                        {'姓名': '赵六', '部门': '市场部'}])

student_df = pd.DataFrame([{'姓名': '张三', '专业': '计算机'},
                          {'姓名': '李四', '专业': '会计'},
                          {'姓名': '王五', '专业': '市场营销'}])

# 右连接(以student_df为基准)
result_right = pd.merge(staff_df, student_df, how='right', on='姓名')
print("右连接结果(保留右边所有记录):")
print(result_right)
右连接结果(保留右边所有记录):
   姓名   部门    专业
0  张三  研发部   计算机
1  李四  财务部    会计
2  王五  NaN  市场营销

结果解读:

  • 学生表的三条记录全部保留
  • 王五没有匹配的部门信息,所以部门列为空
  • 员工表中的赵六(不在学生表中)不出现

3. 高级连接技巧

3.1 处理列名不同的情况

当两个 DataFrame 中作为连接键的列名不同时,可以使用 left_on 和 right_on 参数分别指定。

import pandas as pd

# 创建列名不同的两个DataFrame
staff_df = pd.DataFrame([{'员工姓名': '张三', '部门': '研发部'},
                        {'员工姓名': '李四', '部门': '财务部'}])

student_df = pd.DataFrame([{'学生姓名': '张三', '专业': '计算机'},
                          {'学生姓名': '李四', '专业': '会计'}])

# 连接列名不同时
result_diff_col = pd.merge(staff_df, student_df, 
                          left_on='员工姓名', 
                          right_on='学生姓名',
                          how='inner')
print("列名不同时的连接结果:")
print(result_diff_col)
列名不同时的连接结果:
  员工姓名   部门 学生姓名   专业
0   张三  研发部   张三  计算机
1   李四  财务部   李四   会计

注意:两个连接键列都会保留在结果中。如果要去掉重复列,可以后续处理。

3.2 处理重复列名

当两个 DataFrame 中有相同列名(但不是连接键)时,Pandas 会自动添加后缀 _x 和 _y 来区分。

import pandas as pd

# 创建两个都有"地址"列的DataFrame
staff_df = pd.DataFrame([{'姓名': '张三', '部门': '研发部', '地址': '天津'},
                        {'姓名': '李四', '部门': '财务部', '地址': '北京'},
                        {'姓名': '赵六', '部门': '市场部', '地址': '上海'}])

student_df = pd.DataFrame([{'姓名': '张三', '专业': '计算机', '地址': '天津'},
                          {'姓名': '李四', '专业': '会计', '地址': '上海'},
                          {'姓名': '王五', '专业': '市场营销', '地址': '广州'}])

# 连接时,非连接键的相同列名会自动加后缀
result_with_dup = pd.merge(staff_df, student_df, 
                          how='left', 
                          on='姓名')
print("有重复列名时的连接结果(自动加后缀):")
print(result_with_dup)
有重复列名时的连接结果(自动加后缀):
   姓名   部门 地址_x   专业 地址_y
0  张三  研发部   天津  计算机   天津
1  李四  财务部   北京   会计   上海
2  赵六  市场部   上海  NaN  NaN

后缀说明:

  • 地址_x:来自左边(staff_df)的地址列(实习城市)
  • 地址_y:来自右边(student_df)的地址列(家乡)

你可以使用 suffixes 参数自定义后缀:

import pandas as pd

staff_df = pd.DataFrame([{'姓名': '张三', '部门': '研发部', '地址': '天津'},
                        {'姓名': '李四', '部门': '财务部', '地址': '北京'}])

student_df = pd.DataFrame([{'姓名': '张三', '专业': '计算机', '地址': '天津'},
                          {'姓名': '李四', '专业': '会计', '地址': '上海'}])

# 使用自定义后缀
result_custom_suffix = pd.merge(staff_df, student_df,
                                how='left',
                                on='姓名',
                                suffixes=('_员工', '_学生'))
print("使用自定义后缀:")
print(result_custom_suffix)
使用自定义后缀:
   姓名   部门 地址_员工   专业 地址_学生
0  张三  研发部    天津  计算机    天津
1  李四  财务部    北京   会计    上海

3.3 按索引连接

有时连接键不是列,而是 DataFrame 的索引。这时可以使用 left_index=True 和 right_index=True 参数。

import pandas as pd

# 创建DataFrame并设置索引
staff_df = pd.DataFrame({'部门': ['研发部', '财务部', '市场部']},
                       index=['张三', '李四', '赵六'])

student_df = pd.DataFrame({'专业': ['计算机', '会计', '市场营销']},
                         index=['张三', '李四', '王五'])

print("员工表(索引为姓名):")
print(staff_df)
print("\n学生表(索引为姓名):")
print(student_df)

# 按索引连接
result_by_index = pd.merge(staff_df, student_df,
                          left_index=True,
                          right_index=True,
                          how='outer')
print("\n按索引连接的结果(外连接):")
print(result_by_index)
员工表(索引为姓名):
     部门
张三  研发部
李四  财务部
赵六  市场部

学生表(索引为姓名):
      专业
张三   计算机
李四    会计
王五  市场营销

按索引连接的结果(外连接):
     部门    专业
张三  研发部   计算机
李四  财务部    会计
王五  NaN  市场营销
赵六  市场部   NaN

4. merge() 的两种调用方式

Pandas 提供了两种等价的调用方式:

  1. 函数式调用:pd.merge(left_df, right_df, ...)
  2. 方法式调用:left_df.merge(right_df, ...)

两种方式参数相同,选择哪种取决于个人习惯。方法式调用更符合面向对象思维,能清晰看出以哪个 DataFrame 为基准。

import pandas as pd

staff_df = pd.DataFrame([{'姓名': '张三', '部门': '研发部'},
                        {'姓名': '李四', '部门': '财务部'}])

student_df = pd.DataFrame([{'姓名': '张三', '专业': '计算机'},
                          {'姓名': '李四', '专业': '会计'}])

# 两种调用方式等价
result1 = pd.merge(staff_df, student_df, how='inner', on='姓名')
result2 = staff_df.merge(student_df, how='inner', on='姓名')

print("函数式调用结果:")
print(result1)
print("\n方法式调用结果:")
print(result2)
函数式调用结果:
   姓名   部门   专业
0  张三  研发部  计算机
1  李四  财务部   会计

方法式调用结果:
   姓名   部门   专业
0  张三  研发部  计算机
1  李四  财务部   会计

📝 动手练一练

  1. 概念辨析:假设你有两个 DataFrame,A 表有 100 行,B 表有 80 行。它们有 60 行基于共同键匹配。
    • 如果使用内连接,结果大约有多少行?
    • 如果使用左连接(以 A 为左表),结果有多少行?
    • 如果使用外连接,结果最多可能有多少行?
👉 点击查看参考答案
  • 内连接:约 60 行(取交集,只保留匹配的行)
  • 左连接:100 行(保留左表所有行,右表无匹配的填 NaN)
  • 外连接:最多 120 行(100 + 80 - 60 = 120,这是并集的行数)
  1. 动手实践:创建两个 DataFrame,一个记录产品信息(产品ID、产品名称、价格),一个记录销售记录(销售ID、产品ID、销售量、销售日期)。使用 merge 连接这两个表,计算每个产品的总销售额(价格 × 销售量)。
👉 点击查看参考答案
import pandas as pd

# 产品信息
products = pd.DataFrame({
    '产品ID': ['P001', 'P002', 'P003'],
    '产品名称': ['笔记本', '鼠标', '键盘'],
    '价格': [5000, 150, 300]
})

# 销售记录
sales = pd.DataFrame({
    '销售ID': ['S001', 'S002', 'S003', 'S004'],
    '产品ID': ['P001', 'P002', 'P001', 'P003'],
    '销售量': [2, 5, 1, 3],
    '销售日期': ['2024-01-10', '2024-01-11', '2024-01-12', '2024-01-13']
})

# 连接两个表
merged_data = pd.merge(sales, products, on='产品ID', how='left')

# 计算销售额
merged_data['销售额'] = merged_data['价格'] * merged_data['销售量']

# 按产品汇总
sales_summary = merged_data.groupby(['产品ID', '产品名称'])['销售额'].sum().reset_index()

print("连接后的数据:")
print(merged_data)
print("\n各产品总销售额:")
print(sales_summary)
连接后的数据:
   销售ID  产品ID  销售量        销售日期 产品名称    价格    销售额
0  S001  P001    2  2024-01-10  笔记本  5000  10000
1  S002  P002    5  2024-01-11   鼠标   150    750
2  S003  P001    1  2024-01-12  笔记本  5000   5000
3  S004  P003    3  2024-01-13   键盘   300    900

各产品总销售额:
   产品ID 产品名称    销售额
0  P001  笔记本  15000
1  P002   鼠标    750
2  P003   键盘    900

本章小结

本节深入学习了 Pandas 中数据连接的核心操作 merge()。与简单的 concat() 拼接不同,merge() 是基于共同键的智能连接,是数据分析中整合多源数据的关键技术。

📋 行动清单

  • 掌握四种连接方式:理解外连接、内连接、左连接、右连接的区别,能根据分析需求选择合适的方式。
  • 练习复杂场景:尝试列名不同、按索引连接、处理重复列名等高级用法,理解每个参数的作用。
  • 对比数据库操作:如果你熟悉 SQL,可以对比 merge() 与 SQL JOIN 的异同,加深理解。

在实际工作中,数据往往分散在多个系统或文件中。掌握 merge() 就像拥有了数据世界的”粘合剂”,能够将碎片化的信息整合成完整的分析视图,为后续的统计分析、可视化展示和机器学习建模打下坚实基础。

—— 小象教研组

配套资料 · 直接下载

课件、代码、数据集等配套资料全部免费,点击即可直接下载,无需分享或解锁。

  • 本节课件:数据规整(2) -- 数据连接merge
    PDF · 293KB
    下载
  • 全套课件打包(第1-5章)
    ZIP · 12.8MB
    下载
  • 全套课件打包(第6-8章)
    ZIP · 15MB
    下载
  • 实战数据集:AppleStore 应用商城分析
    ZIP · 329KB
    下载
  • 实战数据集:女性服装电商分析
    ZIP · 2.8MB
    下载
  • Python 数据分析环境搭建指南
    PDF · 2MB
    下载
  • Scrapy 安装教程
    PDF · 12.7MB
    下载
  • 附加实战项目:AppleStore 应用商城数据分析
    ZIP · 0.3MB · ipynb + CSV 数据
    下载
  • 附加实战项目:银行电话营销数据分析
    ZIP · 0.4MB · ipynb + CSV 数据
    下载
  • 附加实战项目:女性服装电商评论数据分析
    ZIP · 2.7MB · ipynb + CSV 数据
    下载
  • 附加实战项目:美国化学学会杂志数据分析
    ZIP · 34.2MB · ipynb + SQLite 数据库
    下载