承接《Pandas 数据合并》,本篇聚焦 concat 拼接(纵向堆叠、横向对齐),区分拼接与 merge 关联,详解参数、内外连接、多层索引拼接、重复索引、拼接后的校验,配套实战和坑点,保持系列教程统一风格。
前置导入与示例数据
import pandas as pd
import numpy as np
df1 = pd.DataFrame({
"id": [1, 2, 3],
"name": ["张三", "李四", "王五"],
"sales": [1000, 1200, 900]
})
df2 = pd.DataFrame({
"id": [4, 5, 6],
"name": ["赵六", "孙七", "周八"],
"sales": [1500, 1100, 1300]
})
df3 = pd.DataFrame({
"id": [1, 2, 3],
"profit": [120, 180, 95]
})
print("df1:")
print(df1)
print("df2:")
print(df2)
print("df3:")
print(df3)
一、concat 基础说明
pd.concat() 是 Pandas 最核心的数据拼接函数,不依靠匹配键,依靠索引对齐。
axis=0:纵向拼接(上下堆叠,增加行),最常用axis=1:横向拼接(左右并排,增加列)
重点区分:
concat:拼接,按索引对齐,类似 SQLUNION ALLmerge:关联,按指定字段匹配,类似 SQLJOIN
基础语法
pd.concat(
objs,
axis=0,
join="outer",
ignore_index=False,
keys=None,
levels=None,
names=None,
verify_integrity=False,
sort=False
)
二、纵向拼接 axis=0(上下合并)
适合多张列结构一致的报表合并,比如每月报表、多批次导出数据。
# 纵向拼接两张销售表
df_stack = pd.concat([df1, df2], axis=0)
print(df_stack)
ignore_index 重置索引(高频必用)
两张表原始索引都是 0,1,2,拼接后索引重复,后续筛选容易出错。
df_stack = pd.concat([df1, df2], axis=0, ignore_index=True)
ignore_index=True:丢弃原来索引,生成从0开始连续新索引。
join 参数:纵向拼接的 join
纵向拼接时 join 控制列的对齐方式
join="outer"(默认):取所有列的并集,不存在的列填充 NaNjoin="inner":只保留两边共同存在的列(列交集)
# 只保留共同列
df_stack_inner = pd.concat([df1, df2], axis=0, join="inner")
keys 参数:来源标记(非常实用)
拼接后新增外层索引,标记这条数据来自哪一张表,方便溯源
df_stack_key = pd.concat([df1, df2], axis=0, keys=["1月","2月"], names=["source"])
print(df_stack_key)
# 取1月数据
print(df_stack_key.loc["1月"])
三、横向拼接 axis=1(左右并排)
横向拼接完全依靠索引对齐,不是按 id 字段匹配!
只有索引相同的行才会对齐到同一行,索引不存在的位置填充 NaN。
df_col = pd.concat([df1, df3], axis=1)
print(df_col)
join 参数:横向拼接的 join
横向拼接控制行索引的交集/并集
join="outer":索引取并集,全部行保留(默认)join="inner":索引取交集,只保留两边都存在的索引行
df_col_inner = pd.concat([df1, df3], axis=1, join="inner")
⚠️ 重大误区:
如果你想按id字段匹配,不要用concat(axis=1),改用pd.merge!
四、verify_integrity 校验索引唯一性
verify_integrity=True:开启索引唯一性检查,如果拼接后索引重复,直接抛出异常,防止脏数据。
适合对索引严格的场景,性能会有损耗。
# 索引重复就报错
pd.concat([df1, df2], axis=0, verify_integrity=True)
五、sort 参数:排序列
sort=True:自动对结果的列名进行字母排序,旧版本默认开启,新版默认 False。
pd.concat([df1, df2], axis=0, sort=True)
六、多表拼接(列表传多个DataFrame)
df_a = pd.DataFrame({"x":[1,2]})
df_b = pd.DataFrame({"x":[3,4]})
df_c = pd.DataFrame({"x":[5,6]})
df_all = pd.concat([df_a, df_b, df_c], axis=0, ignore_index=True)
七、append 废弃提醒
df.append() 在 Pandas 2.0 版本已经移除,不要继续使用,替换方案:
# 旧写法(失效)
# df1.append(df2)
# 新写法
pd.concat([df1, df2], axis=0, ignore_index=True)
八、concat 搭配场景
场景1:批量读取多个csv文件合并
import os
file_list = ["sale1.csv","sale2.csv"]
df_list = []
for f in file_list:
tmp = pd.read_csv(f)
df_list.append(tmp)
df_all = pd.concat(df_list, ignore_index=True)
场景2:横向拼接多个指标表(索引对齐)
df_idx1 = df1.set_index("id")
df_idx3 = df3.set_index("id")
res = pd.concat([df_idx1, df_idx3], axis=1)
九、concat 与 merge / join 对比
| 函数 | 对齐依据 | 方向 | 类比 |
|---|---|---|---|
| pd.concat(axis=0) | 索引 | 上下堆叠 | UNION ALL |
| pd.concat(axis=1) | 索引 | 左右并排 | 索引横向对齐 |
| pd.merge | 指定字段 on | 关联 | JOIN |
| df.join | 索引 | 左右关联 | 索引LEFT JOIN |
十、高频坑清单
axis=1横向拼接误以为是按id匹配,实际是索引对齐,数据错位- 纵向拼接忘记
ignore_index=True,索引重复,后续 loc、合并出错 - 两张表列名大小写、空格不一致,concat 后多出很多NaN列
- keys 生成多层索引,取值时容易忘记外层key
- 大表多次循环concat(低效):先放入列表,最后一次性concat
- 数据类型不一致拼接后自动转为object,后续计算报错
- join=”inner” 容易误删数据,生产环境优先 outer,事后再过滤
十一、综合实战模板
def concat_row(df_list):
"""纵向堆叠,同结构报表合并"""
return pd.concat(df_list, axis=0, ignore_index=True)
def concat_col_by_index(df_list):
"""按索引横向拼接"""
return pd.concat(df_list, axis=1, join="outer")
# 使用示例
df_stack = concat_row([df1, df2])
print(df_stack)
十二、速查表
| 需求 | 写法 |
|---|---|
| 合并多张结构相同的表,上下堆叠 | concat(axis=0, ignore_index=True) |
| 拼接时标记数据来源 | concat(…, keys=[“A”,”B”]) |
| 只保留共同列 | concat(…, join=”inner”) |
| 按索引左右并排 | concat(axis=1) |
| 按字段关联匹配 | pd.merge,不要用concat |
0 条笔记