pd.merge()默认是内连接而非按行拼接,只保留连接键在左右表中都存在的行;常见错误包括类型不一致、列名不同、重复键导致笛卡尔积,应检查dtypes、统一列名、合理选how参数,并避免误用merge替代concat。

merge函数默认是内连接,不是按行拼接
很多人第一次用 pd.merge() 时以为它和 pd.concat() 一样是“堆叠”,结果发现行数变少了——这是因为它默认做的是 SQL 风格的内连接(inner join),只保留左右 DataFrame 在连接键上都存在的行。
常见错误现象:pd.merge(df1, df2, on="id") 返回空或远少于预期的行,但检查发现两边都有 "id" 列,只是部分值为 NaN 或类型不一致(比如一端是 int64,另一端是 object)。
- 连接前务必用
df.dtypes检查连接列类型是否一致,不一致先转成相同类型(如都转str或都用pd.to_numeric(..., errors="coerce")) -
on参数要求列名完全相同;若列名不同,改用left_on和right_on - 默认
how="inner",要外连接必须显式指定how="left"、how="right"或how="outer"
left join 和 outer join 的行为差异很关键
左连接(how="left")保留左表全部行,右表无匹配则补 NaN;全外连接(how="outer")保留左右表所有行,任一侧缺失即补 NaN。实际中容易误选 outer 导致结果膨胀——尤其当某表有重复连接键时,merge 会做笛卡尔积式匹配。
- 如果左表某
id出现 3 次,右表同id出现 2 次,how="left"会生成 3 行(右表字段重复填充),how="outer"会生成 5 行(含右表独有行) - 检查重复键:用
df.groupby("key").size().max() > 1快速判断是否有多对一/多对多情况 - 避免意外爆炸:合并前加
validate="m:1"或validate="1:1"(Pandas ≥ 1.1.0),触发报错而非静默出错
多个DataFrame不能直接 merge,得链式调用或循环
pd.merge() 只接受两个 DataFrame,想合并三个及以上,必须两两进行。常见做法是链式调用,但顺序会影响结果——尤其是用 how="left" 时,中间某次 merge 若丢掉关键行,后续无法挽回。
- 推荐先用
how="outer"合并所有表,再根据业务逻辑过滤;或明确主表,始终以它为 left,其余依次merge(..., how="left") - 避免写成
pd.merge(df1, df2, ...).merge(df3, ...)这种易读性差的长链,封装成函数更可控:
def merge_multiple(dfs, on, how="left"):
result = dfs[0]
for df in dfs[1:]:
result = pd.merge(result, df, on=on, how=how)
return result
- 若各表连接键名不同(如
"user_id"、"uid"、"id"),提前统一重命名比在每次merge中写left_on/right_on更清晰
merge比concat慢得多,别在循环里反复merge小表
每次 pd.merge() 都要建哈希表、对齐索引、处理缺失值,时间复杂度接近 O(n×m);而 pd.concat() 是纯内存拼接,接近 O(n+m)。如果本意只是把结构相同的表“堆起来”,却用了 merge,性能会断崖下跌。
- 典型误用场景:用
merge拼接每日日志表(每张表都有date、event等字段,无关联逻辑),应改用pd.concat([df1, df2, df3], ignore_index=True) - 真正需要
merge的信号:出现on、left_on、right_on,且左右表字段明显不对称(如用户表 + 订单表) - 大数据量时,可先用
df.set_index(key).sort_index()加速 merge,但注意 index 类型需一致
连接逻辑一旦嵌套三层以上,或者涉及空值/重复键/类型混杂,就很容易产出不符合直觉的结果——这时候别急着调参数,先用 df.head() 和 df.isna().sum() 看清楚每一步的中间态。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











