merge 按 key 横向接,how 决定留下谁:inner 只留交集,left 保左表,outer 全留。 两张表的行不重合时,how 选错就会悄悄丢行——所以合并后必须数行数。 concat 按方向摞,axis=0 上下叠行,axis=1 左右拼列,不需要 key。
真实分析很少只在一张表上做。成绩表要知道班主任,得从另一张表接过来。 这一篇讲两个动作:merge(按 key 接)和 concat(按方向摞)。
一、两张表长什么样
左表 10 行:姓名、语文、数学……右表 6 行:姓名、班主任。共同列是「姓名」。 两张表的行不重合:右表多一个孙悦(左表没有),左表后五个二班的人右表也没有。 正因为不重合,才有 how 这个参数要选。完全重合的表不需要选。
如果两张表的行一模一样,四个 how 会给出同样的结果——那时候也没人纠结了。
二、inner:只留两边都有的
df.merge(lead, on='姓名', how='inner') 之后是 5 行——只有一班那五个人。
| 姓名 | 语文 | 班主任 |
|---|---|---|
| 张伟 | 88 | 周老师 |
| 王芳 | 92 | 周老师 |
| 李娜 | 75 | 周老师 |
孙悦被丢了(左表没有),二班五个人也被丢了(右表没有)。 inner 最「安全」:留下的每一行两边都有据可查。但也最容易丢数据。
三、left:以左表为准
how='left' 之后是 10 行——左表一行不少,右表对不上的补空。
二班那五个人的班主任是 NaN,因为他们不在右表里。
left 是日常最常用的。主表不能少行的时候一律用 left。
默认的 how 是 inner,不是 left。这一点坑过很多人:没写 how 就丢了一半数据。
四、outer 和 right
| how | 行数 | 留下的行 |
|---|---|---|
| inner | 5 | 两边都有 |
| left | 10 | 左表全留 |
| right | 6 | 右表全留 |
| outer | 11 | 两边都留 |
outer 是 10 加 1 等于 11:孙悦进来了,成绩那几列是空。 right 和 left 对称,只是以右表为准。实际用得少,一般把主表放左边就行。 merge 之后一定数一下行数。行数变多说明键有重复,这是最常见的合并事故。
五、concat:按方向摞
pd.concat([a, b]) 上下叠,两行加两行变四行。
pd.concat([a, b], axis=1) 左右拼,列数相加,行标签要对得上。
merge 要 key,concat 只要形状对得上。这是两者最本质的区别。
concat 也会保留原行标签,要重排就加 ignore_index=True,和第 3 章加行时一样。
六、动手 Demo:数据合并:merge 与 concat
造一张成绩表和一张班主任表,把四种 how 各跑一遍数行数。 然后用 concat 上下摞、左右拼各演示一次。
import pandas as pd
N = ["张伟", "王芳", "李娜", "刘洋", "陈静",
"杨帆", "赵敏", "黄磊", "周涛", "吴霞"]
DF = pd.DataFrame({"姓名": N,
"语文": [88, 92, 75, 85, 90, 68, 95, 72, 83, 79]})
LEAD = pd.DataFrame({"姓名": N[:5] + ["孙悦"],
"班主任": ["周老师"] * 5 + ["李老师"]})
for how in ["inner", "left", "right", "outer"]:
m = DF.merge(LEAD, on="姓名", how=how)
print("%-6s %2d 行" % (how, len(m)))
M = DF.merge(LEAD, on="姓名", how="left")
print("没班主任的人数 %d" % M["班主任"].isna().sum())
A = DF.head(2)
B = DF.tail(2)
print("上下摞 %s,左右拼 %s" % (pd.concat([A, B]).shape,
pd.concat([A, B], axis=1).shape))
实跑输出:
--------------------------------------------------------------------
数据合并:merge 与 concat
--------------------------------------------------------------------
① 刚才造的两张表
--------------------------------------------------------------------
| 行 | 姓名 | 语文 | 数学 |
|---|---:|---:|---:|
| 0 | 张伟 | 88 | 92 |
| 1 | 王芳 | 92 | 95 |
| 2 | 李娜 | 75 | 68 |
| 行 | 姓名 | 班主任 |
|---|---:|---:|
| 0 | 张伟 | 周老师 |
| 1 | 王芳 | 周老师 |
| 2 | 李娜 | 周老师 |
左表 10 行(成绩),右表 6 行(班主任),共同的列是「姓名」。
注意右表多了一个「孙悦」,左表没有;左表后五个二班的人,右表也没有。
! 两张表的行不重合,正是要演示 how 的原因。完全重合就不需要选了。
② inner:只留两边都有的
--------------------------------------------------------------------
inner 之后 5 行——只有一班那五个人两边都有。
| 行 | 姓名 | 语文 | 班主任 |
|---|---:|---:|---:|
| 0 | 张伟 | 88 | 周老师 |
| 1 | 王芳 | 92 | 周老师 |
| 2 | 李娜 | 75 | 周老师 |
孙悦被丢了(左表没有),二班五个人也被丢了(右表没有)。
! inner 是最「安全」的一种:留下的每一行两边都有据可查。
③ left:以左表为准
--------------------------------------------------------------------
left 之后 10 行——左表 10 行一行不少,右表对不上的补空。
二班那五个人的班主任是 NaN,因为他们不在右表里。
! left 是日常最常用的。主表不能少行的时候一律用 left,别用默认的 inner。
④ outer:两边都留
--------------------------------------------------------------------
outer 之后 11 行——10 加 1,孙悦也进来了,成绩那几列是空。
| how | 行数 | 留下的行 |
|---|---|---|
| inner | 5 | 两边都有 |
| left | 10 | 左表全留 |
| right | 6 | 右表全留 |
| outer | 11 | 两边都留 |
! merge 之后一定数一下行数。行数变多说明键有重复,这是最常见的合并事故。
⑤ concat:上下摞和左右拼
--------------------------------------------------------------------
上下摞:(4, 2)(2 行加 2 行)。
左右拼:(4, 4)(按列拼,行标签要对得上)。
! merge 是「按 key 接」,concat 是「按方向摞」。前者要键,后者只要形状对得上。
isna().sum() 数出来是 5——二班五个人没在右表里,left 之后补了空。
默认 how 是 inner。不写 how 就丢行,而且丢得毫无提示。
30 秒实验
- 把 how 的参数全部去掉,看行数变成几(应该是 5)。
- 给 LEAD 里加一条重复的「张伟」,再 left 合并,看行数怎么变。
- 想一想:为什么行数变多比变少更危险?
常见误区
- 不写 how:默认 inner,主表的行悄悄少了一半。
- 右表的键有重复:合并后行数暴涨,每个重复各配一遍。
- on 写错列名:报 KeyError;如果两边列名不同要用 left_on 和 right_on。
- concat 忘了 ignore_index:行标签重复,后面按标签取会拿到好几行。
自测三问(点开对答案)
1. merge 的 how 四个值分别留什么? —— inner 留交集,left 保左表,right 保右表,outer 两边都留(缺的补空)。 2. merge 和 concat 最本质的区别? —— merge 按共同的键横向接,需要 key;concat 按方向摞,只要形状对得上。 3. 合并后最该检查什么? —— 行数。行数变多说明键有重复,行数变少说明 how 选错了或者键对不上。表接好了,最后两篇讲「存出去」和「按时间看」:导出 Excel、日期、时间序列。
- ☐ (本篇完,下一篇 4.13 数据导出与日期处理)