自学教程 学编程从这里开始

Pandas 数据合并

承接《Pandas 重复数据处理》,本篇系统讲解 Pandas 数据合并:merge、concat、join、append(废弃),对比语法、连接类型、对齐逻辑、多键合并、合并后冲突处理、实战场景,是多表关联、数据拼接的核心章节,保持系列教程统一风格。
前置导入与示例数据

import pandas as pd
import numpy as np

df_left = pd.DataFrame({
    "user_id": [1,2,3,4,5],
    "name": ["张三","李四","王五","赵六","孙七"],
    "city": ["杭州","上海","杭州","北京","广州"]
})
df_right = pd.DataFrame({
    "user_id": [2,3,4,6,7],
    "salary": [12000,15000,11000,9000,13000],
    "level": ["B","A","C","D","B"]
})
print("左表:")
print(df_left)
print("右表:")
print(df_right)

一、四个方法一句话区分

  • pd.merge():按关键字段关联(类似SQL join),支持内连接、左连接、右连接、全外连接,最常用
  • pd.concat():按行或者按列直接拼接,依靠索引对齐,不需要关联键,适合结构相同的表堆叠
  • df.join():简化版merge,默认基于索引关联
  • df.append():Pandas 2.0+ 已经移除,改用 pd.concat

二、pd.merge 详解(SQL式关联)

基础语法

pd.merge(
    left,
    right,
    how="inner",
    on=None,
    left_on=None,
    right_on=None,
    left_index=False,
    right_index=False,
    suffixes=("_x","_y"),
    indicator=False
)

2.1 内连接 how=”inner”(默认)

只保留两边匹配上的记录

df_inner = pd.merge(df_left, df_right, on="user_id", how="inner")
print("内连接:")
print(df_inner)

2.2 左连接 how=”left”(业务最高频)

保留左表全部,右表匹配不到的填充NaN

df_left_join = pd.merge(df_left, df_right, on="user_id", how="left")
print("左连接:")
print(df_left_join)

2.3 右连接 how=”right”

保留右表全部,左表匹配不到填充NaN

df_right_join = pd.merge(df_left, df_right, on="user_id", how="right")

2.4 全外连接 how=”outer”

两边所有数据都保留,匹配不上的填充NaN

df_outer = pd.merge(df_left, df_right, on="user_id", how="outer")

2.5 关联键名字不一样:left_on / right_on

左表id叫uid,右表叫user_id

df_a = pd.DataFrame({"uid":[1,2,3],"val":[10,20,30]})
df_b = pd.DataFrame({"user_id":[2,3,4],"score":[80,90,70]})
res = pd.merge(df_a, df_b, left_on="uid", right_on="user_id", how="left")

2.6 多字段联合关联(复合主键)

df_m = pd.merge(df_left, df_right, on=["user_id","city"], how="left")

2.7 suffixes 重名字段后缀

两个表存在相同字段名,自动加后缀区分,默认 _x、_y

pd.merge(df_left, df_right, on="user_id", suffixes=("_L","_R"))

2.8 indicator 标记数据来源

增加 _merge 列,标记这条数据来自哪边:both / left_only / right_only

df_mark = pd.merge(df_left, df_right, on="user_id", how="outer", indicator=True)
print(df_mark["_merge"].value_counts())

三、pd.concat 拼接(堆叠)

concat 不做匹配,按索引对齐,分纵向拼接(axis=0,行堆叠)、横向拼接(axis=1,列拼接)

# 构造两个结构相同的表
df1 = pd.DataFrame({"id":[1,2],"v":[10,20]})
df2 = pd.DataFrame({"id":[3,4],"v":[30,40]})

3.1 纵向拼接 axis=0(合并多张同结构报表)

df_concat_row = pd.concat([df1, df2], axis=0, ignore_index=True)
  • ignore_index=True:重新生成连续索引,避免索引重复混乱

3.2 横向拼接 axis=1(按索引并排)

df_concat_col = pd.concat([df1, df2], axis=1)

横向concat依赖索引对齐,不是按某一列匹配,和merge完全不一样!

3.3 join 参数(concat的join)

  • join="outer" 默认:索引取并集
  • join="inner":索引取交集,只保留两边索引都存在的行
pd.concat([df1, df2], axis=1, join="inner")

四、df.join() 索引关联

join 本质是 merge 的简写,默认使用索引关联

df_left_idx = df_left.set_index("user_id")
df_right_idx = df_right.set_index("user_id")
res_join = df_left_idx.join(df_right_idx, how="left")

适合:已经把关联键设为索引的场景。

五、merge、concat 选型对比

方法对齐依据用途类比SQL
merge指定字段(on/left_on)多表按主键关联JOIN(INNER/LEFT/RIGHT/FULL)
concat axis=0索引多张相同结构表上下堆叠UNION ALL
concat axis=1索引按索引并排增加列索引对齐横向拼接
join索引索引上的左关联索引LEFT JOIN

六、实战高频场景

场景1:合并多张月度报表(concat)

df_jan = pd.read_csv("sale_jan.csv")
df_feb = pd.read_csv("sale_feb.csv")
df_all = pd.concat([df_jan, df_feb], ignore_index=True)

场景2:订单主表关联用户维度表(merge左连接,最常用)

order_df = pd.DataFrame({"order_id":[101,102,103],"user_id":[1,2,3],"amount":[200,300,150]})
user_df = pd.DataFrame({"user_id":[1,2,4],"name":["张三","李四","老王"]})
order_with_user = pd.merge(order_df, user_df, on="user_id", how="left")

七、合并后常见配套操作

  1. 查看关联匹配情况:indicator=True + value_counts
  2. 去重:合并后容易产生笛卡尔积重复,配合 drop_duplicates
  3. 缺失值填充:左连接未匹配成功会出现NaN,fillna
  4. 笛卡尔积:关联键不唯一时,行数爆炸,是大坑!

八、高频坑清单

  1. concat(axis=1) 误以为是按列值匹配,实际是按索引对齐,数据错乱
  2. merge 关联键存在重复值,触发笛卡尔积,行数暴涨
  3. 忘记 ignore_index,concat后索引重复,筛选、合并出错
  4. 重名字段没有设置suffixes,列被覆盖混淆
  5. 数据类型不一致(int user_id 和 str user_id),匹配不上,全部NaN
  6. append 已经被移除,老代码直接报错,替换成concat
  7. 大表merge性能差:优先把关联键设为索引、排序

九、综合实战模板

def merge_left_table(left_df, right_df, join_keys):
    """左连接模板,业务最常用"""
    return pd.merge(
        left_df,
        right_df,
        on=join_keys,
        how="left",
        suffixes=("_L","_R")
    )

def concat_stack(df_list):
    """多张同结构表纵向堆叠"""
    return pd.concat(df_list, axis=0, ignore_index=True)

# 调用示例
res_merge = merge_left_table(df_left, df_right, ["user_id"])
print(res_merge)

十、速查表

需求推荐写法
按主键关联两张表,保留主表全部merge how=”left”
取两边交集匹配数据merge how=”inner”
合并多张结构一样的报表concat axis=0, ignore_index=True
基于索引横向并排concat axis=1 / df.join
查看哪些记录匹配失败merge(indicator=True)
标签:

0 条笔记