concat和merge
concat 和merge
先明确核心定位
pd.concat():物理拼接,把多个 DataFrame/Series 按「行 / 列方向」直接拼在一起,基于索引对齐,不做值的匹配;pd.merge():逻辑关联,基于列值相等将两个 DataFrame 合并,类似 SQL 的 JOIN 操作,核心是「按值匹配」。
一、pd.concat ():轴向堆叠(行 / 列直接拼)
核心逻辑
把多个结构相似 / 相关的 DataFrame,按「行方向(新增行)」或「列方向(新增列)」拼接,只看索引是否匹配,不关心列值是否相等。简单说就是「把表格粘在一起」,不做数据筛选。
核心语法
pd.concat( objs, # 要合并的DataFrame/Series列表,如 [df1, df2] axis=0, # 0=行拼接(默认),1=列拼接 ignore_index=False, # 是否重置索引(避免重复索引) join="outer" # outer=保留所有列/行(默认),inner=只保留重叠的列/行)示例(基于员工数据)
先准备基础数据:
import pandas as pdimport numpy as np
# 员工基础信息表1(张三、李四、王五)df1 = pd.DataFrame({ "姓名": ["张三", "李四", "王五"], "年龄": [20, 25, 22], "城市": ["北京", "上海", "广州"]}, index=["员工1", "员工2", "员工3"])
# 员工基础信息表2(赵六、钱七)df2 = pd.DataFrame({ "姓名": ["赵六", "钱七"], "年龄": [28, 30], "城市": ["北京", "深圳"]}, index=["员工4", "员工5"])
# 员工薪资表(和df1索引一致)df3 = pd.DataFrame({ "薪资": [8000, 12000, 9500], "绩效": ["良", "优", "中"]}, index=["员工1", "员工2", "员工3"])场景 1:行拼接(axis=0,新增行)
把 df1 和 df2 的行拼在一起(补充新员工):
df_row_concat = pd.concat([df1, df2], axis=0)print("行拼接结果(新增员工行):")print(df_row_concat)输出(保留原有索引,列名一致直接拼):
姓名 年龄 城市员工1 张三 20 北京员工2 李四 25 上海员工3 王五 22 广州员工4 赵六 28 北京员工5 钱七 30 深圳场景 2:列拼接(axis=1,新增列)
把 df1 和 df3 的列拼在一起(给员工加薪资 / 绩效列):
df_col_concat = pd.concat([df1, df3], axis=1)print("\n列拼接结果(新增薪资/绩效列):")print(df_col_concat)输出(按索引对齐,员工 1/2/3 的列拼在一起):
姓名 年龄 城市 薪资 绩效员工1 张三 20 北京 8000 良员工2 李四 25 上海 12000 优员工3 王五 22 广州 9500 中二、pd.merge ():按键关联(类似 SQL JOIN)
核心逻辑
基于列值相等(不是索引)将两个 DataFrame 合并,核心是「按指定列的值匹配行」,比如用「姓名」「员工 ID」把「基础信息表」和「薪资表」关联起来,完全对应 SQL 的 INNER/LEFT/RIGHT/OUTER JOIN。
核心语法
pd.merge( left, # 左表(比如基础信息表) right, # 右表(比如薪资表) on=None, # 关联的键列名(需在两个表中都存在,如"姓名") left_on=None, # 左表的键列(两表键列名不同时用,如左表"姓名",右表"员工姓名") right_on=None, # 右表的键列 how="inner", # 连接方式:inner(默认)/left/right/outer indicator=False # 是否显示每行的合并来源)示例(基于员工数据)
准备关联用的两张表(基础信息 + 薪资绩效):
# 左表:员工基础信息(包含王五,无钱七)df_base = pd.DataFrame({ "姓名": ["张三", "李四", "王五", "赵六"], "年龄": [20, 25, 22, 28], "城市": ["北京", "上海", "广州", "北京"]})
# 右表:员工薪资绩效(无王五,有钱七)df_salary = pd.DataFrame({ "姓名": ["张三", "李四", "赵六", "钱七"], "薪资": [8000, 12000, 15000, 18000], "绩效": ["良", "优", "优", "中"]})场景 1:内连接(inner,默认)
只保留「两张表中都有」的姓名(张三、李四、赵六):
df_inner = pd.merge(df_base, df_salary, on="姓名", how="inner")print("内连接结果(仅保留共同姓名):")print(df_inner)输出:
姓名 年龄 城市 薪资 绩效0 张三 20 北京 8000 良1 李四 25 上海 12000 优2 赵六 28 北京 15000 优场景 2:左连接(left)
保留「左表所有行」,右表无匹配则补 NaN(王五的薪资 / 绩效为 NaN):
df_left = pd.merge(df_base, df_salary, on="姓名", how="left")print("\n左连接结果(保留所有基础表员工):")print(df_left)输出:
姓名 年龄 城市 薪资 绩效0 张三 20 北京 8000.0 良1 李四 25 上海 12000.0 优2 王五 22 广州 NaN NaN3 赵六 28 北京 15000.0 优场景 3:外连接(outer)
保留「两张表所有行」,无匹配则补 NaN(王五、钱七都保留):
df_outer = pd.merge(df_base, df_salary, on="姓名", how="outer")print("\n外连接结果(保留所有员工):")print(df_outer)输出:
姓名 年龄 城市 薪资 绩效0 张三 20.0 北京 8000.0 良1 李四 25.0 上海 12000.0 优2 王五 22.0 广州 NaN NaN3 赵六 28.0 北京 15000.0 优4 钱七 NaN NaN 18000.0 中三、concat vs merge 核心对比表
| 维度 | pd.concat() | pd.merge() |
|---|---|---|
| 核心逻辑 | 轴向堆叠(行 / 列直接拼接) | 按键值匹配(类似 SQL JOIN) |
| 对齐依据 | 基于索引(行 / 列索引) | 基于列值(指定的键列) |
| 轴方向 | 支持 axis=0(行)/axis=1(列) | 仅按列方向合并(新增列) |
| 列名要求 | 行拼接:列名可不同(补 NaN);列拼接:无强制要求 | 需指定键列(列名可不同,用 left_on/right_on) |
| 适用场景 | 1. 同结构 DF 的行拼接(新增员工行)2. 同索引 DF 的列拼接(新增薪资列) | 1. 关联不同表的相关数据(基础信息 + 薪资)2. 按列值筛选合并(只保留共同员工) |
| 类似 SQL 操作 | UNION ALL(行拼接)/ 横向拼接(列) | INNER/LEFT/RIGHT/OUTER JOIN |
总结
- 选 concat 的场景:你需要「简单拼接」数据(行 / 列直接粘),比如把多个同结构的员工表拼在一起、给同索引的员工表加列,核心是「不筛选,只堆叠」;
- 选 merge 的场景:你需要「按值关联」数据,比如用姓名 / 员工 ID 把基础信息和薪资表合并、只保留两张表都有的员工,核心是「按键匹配,可筛选」;
- 关键判断:合并时是否需要「按列值筛选匹配」—— 不需要就用 concat,需要就用 merge。

















