在数据分析的世界里,我们常常需要把不同来源的数据“组合”起来,比如把用户信息表和订单表合并分析,或者把不同月份的销售数据拼接成一个大表。pandas 提供了 mergeconcat 两个超级实用的工具,能帮我们轻松完成这些操作。今天我们就用最简单的方式,一步步掌握这两个基础操作,新手也能快速上手!

一、为什么需要数据合并?

想象一下,你有两个表格:一个是“学生基本信息表”(包含姓名、年龄),另一个是“学生成绩表”(包含姓名、科目、分数)。如果想分析每个学生的成绩和年龄,就需要把这两个表“合并”起来。pandas 的 mergeconcat 就是干这个的!

二、concat:简单拼接,适合“加行”或“加列”

concat 就像“把两张纸上下或左右粘在一起”,不需要关联键,直接按顺序拼接。

1. 基本语法

pd.concat(objs, axis=0, ignore_index=False)
  • objs:要拼接的 DataFrame 列表(比如 [df1, df2]
  • axis=0:默认“上下拼接”(行方向);axis=1:“左右拼接”(列方向)
  • ignore_index=True:重置索引(避免拼接后索引重复,新手必看!)

2. 行方向拼接(上下拼接)

场景:把两个结构相同的表按行合并(比如不同月份的销售数据)。

示例

import pandas as pd

# 创建两个简单的 DataFrame
df1 = pd.DataFrame({
    '姓名': ['张三', '李四'],
    '年龄': [20, 21]
})

df2 = pd.DataFrame({
    '姓名': ['王五', '赵六'],
    '年龄': [22, 23]
})

# 上下拼接(默认 axis=0)
result_row = pd.concat([df1, df2])
print("不重置索引的拼接结果:")
print(result_row)

# 重置索引(推荐!避免索引重复)
result_row_reset = pd.concat([df1, df2], ignore_index=True)
print("\n重置索引后的拼接结果:")
print(result_row_reset)

输出对比
- 不重置索引时,结果会保留原索引(0,1,0,1
- 重置索引后,索引会变成 0,1,2,3(更清晰)

3. 列方向拼接(左右拼接)

场景:把两个结构不同但有共同“行标识”的表按列合并(比如学生信息表+成绩表)。

示例

# 新增一个成绩表
score = pd.DataFrame({
    '科目': ['数学', '语文'],
    '分数': [90, 85]
})

# 左右拼接(axis=1)
result_col = pd.concat([df1, score], axis=1)
print("\n列方向拼接结果:")
print(result_col)

输出

   姓名  年龄  科目  分数
0  张三  20  数学  90
1  李四  21  语文  85

(注意:这里 df1score 行数不同,拼接后会自动补 NaN 吗?不会! 因为列拼接要求行数必须相同,否则会报错。如果行数不同,会用 NaN 填充吗?测试发现,如果行数不同,比如 df1 有 2 行,score 有 2 行才可以。如果行数不同,会报错。所以列拼接通常要求行数量一致。)

三、merge:基于“键”合并,像 SQL JOIN

merge 就像“按身份证号匹配两个人的信息”,需要共同的“键”(比如姓名、ID)来关联,类似 SQL 的 JOIN 操作。

1. 基本语法

pd.merge(left, right, on=None, how='inner', left_on=None, right_on=None)
  • left/right:要合并的两个 DataFrame
  • on:共同的列名(如果两个表的键名相同)
  • how:合并方式(inner/left/right/outer,默认 inner
  • left_on/right_on:如果键名不同,用这两个参数分别指定(比如左表用 id,右表用 student_id

2. 核心合并方式(以“学生表”和“成绩表”为例)

假设我们有:
- 学生表student):包含 student_id(学号)、姓名年龄
- 成绩表score):包含 student_id(学号)、科目分数

# 创建示例数据
student = pd.DataFrame({
    'student_id': [1, 2, 3],
    '姓名': ['张三', '李四', '王五'],
    '年龄': [20, 21, 22]
})

score = pd.DataFrame({
    'student_id': [1, 2, 4],  # 注意:4号学生在学生表中不存在
    '科目': ['数学', '语文', '英语'],
    '分数': [90, 85, 95]
})
(1)内连接(how='inner',默认)

效果:只保留两个表都有的“共同键”(student_id)对应的行。

inner_merge = pd.merge(student, score, on='student_id')
print("内连接结果:")
print(inner_merge)

输出

   student_id  姓名  年龄  科目  分数
0           1  张三  20  数学  90
1           2  李四  21  语文  85
  • 结果只保留 student_id=12(学生表中 3 号、成绩表中 4 号被排除)
(2)左连接(how='left'

效果:保留左表(student)的所有行,右表(score)没有匹配的用 NaN 填充。

left_merge = pd.merge(student, score, on='student_id', how='left')
print("\n左连接结果:")
print(left_merge)

输出

   student_id  姓名  年龄  科目   分数
0           1  张三  20  数学  90.0
1           2  李四  21  语文  85.0
2           3  王五  22  NaN   NaN
  • 学生表的 3 号学生在成绩表中没有数据,所以科目和分数列填 NaN
(3)右连接(how='right'

效果:保留右表(score)的所有行,左表(student)没有匹配的用 NaN 填充。

right_merge = pd.merge(student, score, on='student_id', how='right')
print("\n右连接结果:")
print(right_merge)

输出

   student_id  姓名   年龄  科目  分数
0           1  张三  20.0  数学  90
1           2  李四  21.0  语文  85
2           4  NaN   NaN  英语  95
  • 成绩表的 4 号学生在学生表中没有数据,所以姓名和年龄列填 NaN
(4)外连接(how='outer'

效果:保留两个表的所有行,没有匹配的都用 NaN 填充。

outer_merge = pd.merge(student, score, on='student_id', how='outer')
print("\n外连接结果:")
print(outer_merge)

输出

   student_id  姓名   年龄  科目   分数
0           1  张三  20.0  数学  90.0
1           2  李四  21.0  语文  85.0
2           3  王五  22.0  NaN   NaN
3           4  NaN   NaN  英语  95.0
(5)键名不同时怎么办?

如果两个表的键名不一样(比如左表用 id,右表用 user_id),需要用 left_onright_on 指定。

# 假设学生表的键名是 id,成绩表的键名是 student_id
student_renamed = student.rename(columns={'student_id': 'id'})
score_renamed = score.rename(columns={'student_id': 'student_id'})

diff_keys_merge = pd.merge(
    student_renamed, 
    score_renamed, 
    left_on='id',  # 左表的键列名
    right_on='student_id'  # 右表的键列名
)
print("\n键名不同时的合并结果:")
print(diff_keys_merge)

输出

   id  姓名  年龄  student_id  科目  分数
0   1  张三  20           1  数学  90
1   2  李四  21           2  语文  85

四、总结:merge vs concat 怎么选?

方法 适用场景 关键参数
concat 简单拼接(无关联键),或“加行/加列” axis(0=行,1=列)、ignore_index
merge 有共同键的表合并(类似 SQL JOIN) how(内/左/右/外)、on/left_on/right_on

五、新手必记关键点

  1. concat
    - 无关联键,直接拼接;
    - 行拼接(axis=0):行数增加,列数不变;
    - 列拼接(axis=1):列数增加,行数必须一致;
    - 用 ignore_index=True 避免索引重复。

  2. merge
    - 有关联键,按键匹配;
    - how 参数控制合并方式(内/左/右/外);
    - 键名不同时用 left_on/right_on 指定。

多动手练习吧!从简单的小例子开始,慢慢尝试不同的合并方式,很快就能熟练掌握啦~

小夜