📊 第4章 Pandas 进阶
📄 一句话概括

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 秒实验

  1. 把 how 的参数全部去掉,看行数变成几(应该是 5)。
  2. 给 LEAD 里加一条重复的「张伟」,再 left 合并,看行数怎么变。
  3. 想一想:为什么行数变多比变少更危险?

常见误区

  1. 不写 how:默认 inner,主表的行悄悄少了一半。
  2. 右表的键有重复:合并后行数暴涨,每个重复各配一遍。
  3. on 写错列名:报 KeyError;如果两边列名不同要用 left_on 和 right_on。
  4. concat 忘了 ignore_index:行标签重复,后面按标签取会拿到好几行。
自测三问(点开对答案) 1. merge 的 how 四个值分别留什么? —— inner 留交集,left 保左表,right 保右表,outer 两边都留(缺的补空)。 2. merge 和 concat 最本质的区别? —— merge 按共同的键横向接,需要 key;concat 按方向摞,只要形状对得上。 3. 合并后最该检查什么? —— 行数。行数变多说明键有重复,行数变少说明 how 选错了或者键对不上。

表接好了,最后两篇讲「存出去」和「按时间看」:导出 Excel、日期、时间序列。

相关笔记