真实项目的数据从来不是一张表:用户信息在一张表、订单在另一张、月份拆在十二个文件里。第 8 章教你三件事:把多张表拼起来(join/concat)、把一张表变个形状(reshape/pivot)、用层次索引优雅驾驭嵌套结构。
🏢 层次索引:一列顶两列
MultiIndex 让一个索引里带多层,高维数据优雅「住」进二维表:
data = pd.Series(rng.standard_normal(6),
index=[["a","a","b","b","c","c"],
[1,2,1,2,1,2]])
data["b"] # 外层取:b 层的全部
data["b", "1"] # 内层精确定位
data.unstack() # 内层索引「弹」成列 → 变成 3×2 表
data.unstack().stack() # 再「压」回去,完美互逆
层次索引的实用三招:swaplevel() 交换层、sort_index(level=0) 分层排序(先排序再定位是性能铁律)、df.set_index(["key1","key2"]) 把普通列升格为索引。
🔗 merge:数据库风格的连接
pandas 的 merge 就是 SQL 的 JOIN。以「多对一」「多对多」最常见:
df1 = pd.DataFrame({"key": ["b","b","a","c","a"], "data1": range(5)})
df2 = pd.DataFrame({"key": ["a","b","d"], "data2": range(3)})
pd.merge(df1, df2) # 默认 inner,按同名列 key 连接
pd.merge(df1, df2, on="key", how="left") # 左连接:保留 df1 全部行
pd.merge(df1, df2, how="outer") # 外连接:并集
pd.merge(df1, df2, how="inner") # 内连接:交集(默认)
how 参数的四张脸:
inner:两边 key 都有的行(默认);left:以左表为准,右边没有就 NaN;right:镜像版 left;outer:两边全保留。
两边的 key 列名不同用 left_on/right_on;列名撞车用 suffixes=("_left","_right") 区分。多对多时行数会按笛卡尔积膨胀——merge 之后 len() 一下是好习惯。
按索引合并
一边的连接键在索引里时,用 left_index=True / right_index=True:
pd.merge(left, right, left_on="key", right_index=True) # 混合式
left.join(right) # 简便写法:按索引连接,还能一次连多张表
🧲 concat:沿轴堆叠
merge 处理「按 key 找对象」,concat 处理「直接摞/拼」:
s1, s2, s3 = pd.Series(...), pd.Series(...), pd.Series(...)
pd.concat([s1, s2, s3]) # 纵向摞成一根长 Series
pd.concat([s1, s2], axis=1) # 横向拼成表(按索引对齐!)
pd.concat([s1, s2], axis=1, join="inner") # 只留共享索引
pd.concat([s1, s2], keys=["one","two"]) # 加外层标签记录来源
df = df[~df.index.duplicated()] # 拼完去重的常规操作
join_axes(第3版中被 reindex 替代)与 ignore_index=True(重排 0..n 索引)是另两个常用参数。
🩹 combine_first:逐格补洞
两张表都有部分数据、各有缺口时:
a.combine_first(b) # a 的值优先,a 缺的位置用 b 的补上
相当于「逐元素版的 df.update + 按索引对齐」,合并多个来源的脏数据极好用。
🏗️ 重塑:stack / pivot / melt
stack 与 unstack:在「列」和「内层行索引」之间旋转数据。stack 把最内列压进行索引(宽变窄),unstack 反之——第 5 章见过,这里是完整用法。
长表 → 宽表(pivot):这是「一行一个观测」的长格式数据最经典的变换:
ldata # columns: date, item, value(长格式:每个 (date,item) 一行)
pivoted = ldata.pivot(index="date", columns="item", values="value")
# 每个 item 变成一列!
宽表 → 长表(melt):pivot 的逆操作,统计软件和绘图(seaborn 的 tidy data)都偏爱长格式:
df = pd.DataFrame({"key": ["foo","bar"],
"A": [1,2], "B": [3,4]})
melted = pd.melt(df, id_vars="key")
# key variable value
# 0 foo A 1
# 1 bar A 2
# 2 foo B 3
# 3 bar B 4
📌 本章小结:merge 四连接 inner/left/right/outer,key 不同用
left_on/right_on,撞名用suffixes;concat 按 axis 摞或拼、keys标来源;combine_first逐格补洞;pivot 长转宽、melt 宽转长、stack/unstack 在列与索引间旋转——长宽表变换想清楚「哪一列该变成行标签、哪个维度该展开成列」就永远不会晕。