merge按字段值对齐,concat按索引位置拼接;merge支持多键、类型转换和语义连接,concat仅物理堆叠且易错位。

merge能按字段值对齐,concat只认索引或位置
这是最根本的差异。当你有两张表,比如 users 和 orders,它们的共同字段是 user_id,但各自索引不一致(users 索引是 0,1,2,orders 索引是 0,0,1),pd.concat(..., axis=1) 会直接按索引位置硬拼——第 0 行对第 0 行,结果错位;而 pd.merge(..., on='user_id') 会扫描值、匹配 user_id==1 的所有行,不管它在哪个索引位置。
常见错误现象:concat([df1.set_index('id'), df2.set_index('id')], axis=1) 看似“按 id 对齐”,实则依赖索引完全一致。一旦某张表缺失某个 id 或顺序打乱,就会出现 NaN 或错行。
- 使用场景:用户表和订单表关联、日志表和配置表映射、多源指标按 ID 补充维度
- 参数差异:
merge必须指定on、left_on/right_on;concat的join='inner'只控制索引交集,不看字段值
merge支持4种连接方式,concat只有“堆”和“并”两种模式
pd.merge 的 how 参数决定了逻辑关系:'inner' 取交集,'left' 保左表全量,'outer' 全保留,'right' 保右表全量。这对应真实业务中“只看有效订单”“查所有用户含空订单”“合并全部记录不留死角”等需求。
而 pd.concat 的 axis=0 或 axis=1 是物理拼接,没有语义判断。即使你用 join='inner',也只是取索引交集,不是按业务键过滤。
- 容易踩的坑:误以为
concat(..., join='inner')能替代merge(..., how='inner')—— 前者丢的是索引不重合的行,后者丢的是键不匹配的行,二者完全不是一回事 - 性能影响:小数据时差异不明显;大数据下
merge默认走哈希连接(快),concat是纯内存拷贝(轴向越大越慢)
merge能自动处理列名冲突,concat需要手动干预
两张表都有 name 列,merge 默认用 suffixes=('_x', '_y') 生成 name_x 和 name_y;你也可以自定义成 suffixes=('_left', '_right')。
concat 遇到同名列会直接保留原名,导致列重复、后续操作报错(如 df['name'] 返回 Series 而非标量),必须提前重命名或删列。
- 典型场景:合并不同系统导出的用户表(都含
phone字段但含义不同)、A/B 实验两组指标表(都含conversion_rate) - 实操建议:用
merge时显式写suffixes,避免默认后缀引发歧义;用concat前先检查df.columns,必要时用add_prefix()或rename(columns={...})
merge可跨列、多列、混合类型关联,concat做不到
pd.merge 支持 on=['user_id', 'date'] 多键合并,也支持 left_on='user_id' + right_on='customer_id' 列名不同但语义相同的场景,甚至能处理 left_on='timestamp' + right_on='date' 这类类型不一致但需对齐的情况(配合 pd.to_datetime 预处理)。
concat 没有“关联”概念,它只关心轴方向和索引对齐。想实现类似效果,得先用 set_index 把字段转索引,再 concat(..., axis=1),但前提是这些字段值必须一一对应且无重复——现实中极少满足。
- 容易被忽略的地方:多键合并时,
merge会自动去重组合键;而concat即使索引是 MultiIndex,也无法基于值做跨表匹配 - 兼容性注意:
merge对category、datetime类型字段友好;concat在拼接含 category 的 DataFrame 时可能触发 dtype 转换,导致内存暴涨
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











