第十四章:数据合并:merge / join(Pandas)

第十四章:数据合并:merge / join(Pandas)

    • [14.1 本章你将学会什么](#14.1 本章你将学会什么)
    • [14.2 一句话先讲清:merge / join / concat 区别是什么?](#14.2 一句话先讲清:merge / join / concat 区别是什么?)
    • [14.3 连接类型(inner/left/right/outer)到底差在哪?](#14.3 连接类型(inner/left/right/outer)到底差在哪?)
    • [14.4 准备两张"科研风格"的示例表](#14.4 准备两张“科研风格”的示例表)
    • [14.5 最常用:pd.merge(明确写清 keys)](#14.5 最常用:pd.merge(明确写清 keys))
      • [14.5.1 inner:只保留两边都出现的 sid](#14.5.1 inner:只保留两边都出现的 sid)
      • [14.5.2 left:以左表为准(科研里最常见)](#14.5.2 left:以左表为准(科研里最常见))
      • [14.5.3 outer:保留并集,用于"找差异、做核对"](#14.5.3 outer:保留并集,用于“找差异、做核对”)
    • [14.6 多键合并:不止 sid,一个键不够怎么办?](#14.6 多键合并:不止 sid,一个键不够怎么办?)
    • [14.7 合并前必须检查的 3 件事(不做就等着翻车)](#14.7 合并前必须检查的 3 件事(不做就等着翻车))
      • [14.7.1 键的 dtype 是否一致(最隐蔽的坑)](#14.7.1 键的 dtype 是否一致(最隐蔽的坑))
      • [14.7.2 键是否唯一:1:1、1:m、m:m 你得心里有数](#14.7.2 键是否唯一:1:1、1:m、m:m 你得心里有数)
      • [14.7.3 用 validate 强制约束合并关系(强烈推荐)](#14.7.3 用 validate 强制约束合并关系(强烈推荐))
    • [14.8 列名冲突:suffixes 一次解决](#14.8 列名冲突:suffixes 一次解决)
    • [14.9 join:当索引就是键时,用它更顺手](#14.9 join:当索引就是键时,用它更顺手)
    • [14.10 研究型 Checklist:合并质量自检清单](#14.10 研究型 Checklist:合并质量自检清单)
    • [14.11 本章练习(建议你真跑一遍)](#14.11 本章练习(建议你真跑一遍))
      • [练习 1:用 outer + indicator 找出不一致 sid](#练习 1:用 outer + indicator 找出不一致 sid)
      • [练习 2:用 validate 保证 1:m 合并](#练习 2:用 validate 保证 1:m 合并)
      • [练习 3:处理列名冲突](#练习 3:处理列名冲突)
    • [14.12 小结:你应该带走的"工程化结论"](#14.12 小结:你应该带走的“工程化结论”)
    • 下一篇:

在真实科研数据里,"清洗"只是前菜;真正让数据变得可分析的,往往是把来自不同来源的表 对齐并合并

这一章我们用一套可复现的小例子,把 merge / join / concat 讲透,并把最常踩的坑一次性踩完、填平。


14.1 本章你将学会什么

  • 理解 主键(key)参照完整性:为什么"能合并"不代表"合并对"
  • 掌握 pd.merge()4 种连接inner / left / right / outer
  • 处理真实场景的常见问题:重复键、列名冲突、缺失键、类型不一致
  • 学会用 indicator=True 做合并质量检查(科研数据必备)

14.2 一句话先讲清:merge / join / concat 区别是什么?

  • concat上下拼接或左右拼接(按轴叠起来),不负责"对齐键"
  • merge:按某个(些)键把两张表 像 SQL JOIN 一样对齐后合并
  • join:更偏向 按索引(index)合并 (当然也能指定 on,但本质是 merge 的语法糖)

记住一句话:
科研数据合并,优先考虑 merge;只有当索引就是键时,再用 join。


14.3 连接类型(inner/left/right/outer)到底差在哪?

你可以把合并想象成"以键为准,决定保留哪些行"。
outer
保留并集 keys
right
保留右表全部 keys + 匹配到的左表
left
保留左表全部 keys + 匹配到的右表
inner
只保留交集 keys
左表 keys
右表 keys


14.4 准备两张"科研风格"的示例表

我们用一个常见场景:

  • 表 A:样本基本信息(subject-level)
  • 表 B:测量指标(measurement-level)
python 复制代码
import pandas as pd

df_subject = pd.DataFrame({
    "sid": [101, 102, 103, 104],
    "group": ["control", "control", "treatment", "treatment"],
    "age": [23, 25, 22, 24]
})

df_score = pd.DataFrame({
    "sid": [101, 102, 102, 105],
    "score": [88, 91, 95, 77],
    "timepoint": ["T1", "T1", "T2", "T1"]
})

df_subject, df_score

注意:df_score 里有:

  • sid=102 重复键(同一被试多时间点)
  • sid=105 在 A 表不存在(右表多出来的键)

14.5 最常用:pd.merge(明确写清 keys)

14.5.1 inner:只保留两边都出现的 sid

python 复制代码
inner = pd.merge(df_subject, df_score, on="sid", how="inner")
inner

适用场景:

  • 你只关心"有完整信息且有测量记录"的样本
  • 做回归、ANOVA 时常用(但要警惕样本被你默默删掉)

14.5.2 left:以左表为准(科研里最常见)

python 复制代码
left = pd.merge(df_subject, df_score, on="sid", how="left")
left

适用场景:

  • 你要保留全部被试,只是某些被试缺测(score 为 NaN)
  • 后续再决定缺失处理策略(删除 / 插补 / 分析缺失机制)

14.5.3 outer:保留并集,用于"找差异、做核对"

python 复制代码
outer = pd.merge(df_subject, df_score, on="sid", how="outer", indicator=True)
outer

这句 indicator=True 非常关键:它会生成一列 _merge,告诉你每行来自哪里:

  • both:两边都有
  • left_only:只在左表
  • right_only:只在右表

科研数据合并我建议形成习惯:
先 outer + indicator 做核对,再决定用 left/inner 进入正式分析。


14.6 多键合并:不止 sid,一个键不够怎么办?

典型例子:同一被试多个时间点,多次测量要严格对齐。

python 复制代码
df_score2 = pd.DataFrame({
    "sid": [101, 102, 102, 103],
    "timepoint": ["T1", "T1", "T2", "T1"],
    "score": [88, 91, 95, 90]
})

df_demo = pd.DataFrame({
    "sid": [101, 102, 102, 103],
    "timepoint": ["T1", "T1", "T2", "T1"],
    "sleep_hours": [7.0, 6.5, 6.0, 7.5]
})

multi = pd.merge(df_score2, df_demo, on=["sid", "timepoint"], how="inner")
multi

当你的测量表是"长表"(long format)时,多键合并非常常见。


14.7 合并前必须检查的 3 件事(不做就等着翻车)

14.7.1 键的 dtype 是否一致(最隐蔽的坑)

python 复制代码
df_subject["sid"].dtype, df_score["sid"].dtype

如果一边是 int,另一边是 str,合并会出现"看似没报错、但全是 NaN"的假象。

统一类型:

python 复制代码
df_subject["sid"] = df_subject["sid"].astype(str)
df_score["sid"] = df_score["sid"].astype(str)

14.7.2 键是否唯一:1:1、1:m、m:m 你得心里有数

python 复制代码
df_subject["sid"].is_unique, df_score["sid"].is_unique
  • df_subject 是 1(subject-level)
  • df_score 是 m(多测量)

于是合并的结果是 1:m,行数会增加,这是正常的。

但如果两边都是重复键,就会变成 m:m,行数会指数级膨胀(灾难级)。


14.7.3 用 validate 强制约束合并关系(强烈推荐)

python 复制代码
pd.merge(df_subject, df_score, on="sid", how="left", validate="one_to_many")

可选:

  • "one_to_one"
  • "one_to_many"
  • "many_to_one"
  • "many_to_many"(不建议,除非你真的确认)

这对科研数据特别重要:它让错误尽早暴露,而不是让你在统计结果阶段才发现"样本数不对"。


14.8 列名冲突:suffixes 一次解决

当左右表有同名列(比如都叫 agedatesource),合并后会自动加后缀。你也可以自己定义:

python 复制代码
df_a = pd.DataFrame({"sid":[1,2], "age":[20,21]})
df_b = pd.DataFrame({"sid":[1,2], "age":[200,210]})

pd.merge(df_a, df_b, on="sid", suffixes=("_subj", "_measure"))

14.9 join:当索引就是键时,用它更顺手

python 复制代码
left = df_subject.set_index("sid")
right = df_score.set_index("sid")

joined = left.join(right, how="left")
joined

经验结论:

  • 你的键在列里:用 merge
  • 你的键在 index 里:用 join

14.10 研究型 Checklist:合并质量自检清单

合并完成后,至少回答这 5 个问题:

  1. 合并前后行数是否符合预期?(特别是 1:m、m:m)
  2. _mergeleft_only/right_only 是否在合理范围?
  3. key 的缺失值是否被引入?(是否要先 dropna 还是保留)
  4. 是否出现意外重复?(同一 sid×timepoint 是否变多)
  5. 是否存在 dtype 被无意改变?(如日期字符串变对象)

14.11 本章练习(建议你真跑一遍)

练习 1:用 outer + indicator 找出不一致 sid

  • 输出只在 df_subject 的 sid
  • 输出只在 df_score 的 sid

练习 2:用 validate 保证 1:m 合并

  • merge 在你不小心把左表也变成重复键时立刻报错

练习 3:处理列名冲突

  • 人为制造同名列,使用 suffixes 输出一个可读的表

14.12 小结:你应该带走的"工程化结论"

  • 科研合并的第一原则:先核对,再合并(outer + indicator)
  • 合并的第二原则:把关系说清楚(validate)
  • 合并的第三原则:键的 dtype 要一致、键的唯一性要明确

下一篇:

《第十五章:可视化入门:Matplotlib 做出像样的图》

相关推荐
NAGNIP3 小时前
一文搞懂深度学习中的通用逼近定理!
人工智能·算法·面试
冬奇Lab4 小时前
一天一个开源项目(第36篇):EverMemOS - 跨 LLM 与平台的长时记忆 OS,让 Agent 会记忆更会推理
人工智能·开源·资讯
冬奇Lab4 小时前
OpenClaw 源码深度解析(一):Gateway——为什么需要一个"中枢"
人工智能·开源·源码阅读
AngelPP8 小时前
OpenClaw 架构深度解析:如何把 AI 助手搬到你的个人设备上
人工智能
宅小年8 小时前
Claude Code 换成了Kimi K2.5后,我再也回不去了
人工智能·ai编程·claude
九狼8 小时前
Flutter URL Scheme 跨平台跳转
人工智能·flutter·github
ZFSS8 小时前
Kimi Chat Completion API 申请及使用
前端·人工智能
天翼云开发者社区9 小时前
春节复工福利就位!天翼云息壤2500万Tokens免费送,全品类大模型一键畅玩!
人工智能·算力服务·息壤
知识浅谈10 小时前
教你如何用 Gemini 将课本图片一键转为精美 PPT
人工智能
Ray Liang10 小时前
被低估的量化版模型,小身材也能干大事
人工智能·ai·ai助手·mindx