透视表:pivot 与 pivot_table
本教程共 54 篇 · 第 30 篇 · 更新于 2026-08-11 · 约 6 分钟阅读
本节目标:会用 pivot 把长表重排成宽表,会用 pivot_table 对重复组合做聚合,掌握多级透视和 margins 边际汇总。
pivot:把长表重排成宽表
先看一张长表:张三和李四的语文、数学成绩,每个学生每个科目一行。
import pandas as pd
df = pd.DataFrame({
"姓名": ["张三", "张三", "李四", "李四"],
"科目": ["语文", "数学", "语文", "数学"],
"成绩": [80, 75, 90, 85],
})
print(df)
想变成”一行一个学生、一列一个科目”的宽表,用 pivot:
wide = df.pivot(index="姓名", columns="科目", values="成绩")
print(wide)
pivot 的三个参数就是变形的三要素:
- index:转成新表行索引的列;
- columns:转成新表列名的列,它的唯一值成为新列名;
- values:填进单元格的数值列。
pivot 只做重排,不做任何计算。它要求每个 (index, columns) 组合在原表里必须唯一——张三语文只能有一行,否则表格不知道该填哪个值。不满足时会直接报错:
dup = pd.DataFrame({
"姓名": ["张三", "张三"],
"科目": ["语文", "语文"],
"成绩": [80, 75],
})
try:
dup.pivot(index="姓名", columns="科目", values="成绩")
except ValueError as e:
print("报错:", e)
Warning有重复组合时 pivot 直接报错(Index contains duplicate entries, cannot reshape)。先查重复,再改用 pivot_table。
不写 values:多列一起透视
原表里除了 index、columns 用到的列,还有多个数值列时,可以不写 values。结果生成多层列索引,最外层是数值列名:
df["排名"] = [2, 5, 1, 4]
wide2 = df.pivot(index="姓名", columns="科目")
print(wide2)
这样”成绩”和”排名”各有一套科目列。取其中一套用 wide2[“成绩”],效果和指定 values 一样。这套 MultiIndex 结构,正好是 §31 stack/unstack 的用武之地。
透视结果怎么用
透视完的表还是普通 DataFrame(顶多带着 MultiIndex 列),该有的操作都能用:
print(wide.loc["张三"]) # 取一个学生的所有科目
print(wide["数学"]) # 取一列
透视后列的顺序默认按取值升序排。想让顺序固定(比如按字母序),先把列转成 Categorical(§50)再透视。想变回长表,用 §31 的 stack 或 reset_index + melt 都行。
pivot_table:有重复值就聚合
pivot 解决不了重复值,pivot_table 可以:把同一组合下的多个值,按 aggfunc 聚合成一个。
比如张三考了两次语文(80、90),取平均:
df2 = pd.DataFrame({
"姓名": ["张三", "张三", "张三", "李四", "李四", "李四"],
"科目": ["语文", "语文", "数学", "语文", "数学", "数学"],
"成绩": [80, 90, 100, 70, 85, 95],
})
pt = df2.pivot_table(index="姓名", columns="科目", values="成绩", aggfunc="mean")
print(pt)
aggfunc 默认是 mean(平均值),常用的还有 “sum”、“min”、“max”、“count”、“std”。也可以传自定义函数,比如 aggfunc=lambda x: x.max() - x.min() 求极差。
aggfunc 还能传列表,一次算多种统计:
pt_multi = df2.pivot_table(
index="姓名", columns="科目", values="成绩", aggfunc=["sum", "mean"],
)
print(pt_multi)
结果列变成两层:外层是统计方式,内层是科目。
不写 values 时,pivot_table 会把所有数值列都拿来聚合(列上再多一层)。和 pivot 一样,不写 values 意味着结果带着 MultiIndex 列。
多级透视:index 和 columns 传列表
index、columns 都可以传多个列名,得到多级行列索引:
df3 = pd.DataFrame({
"班级": ["1班"] * 4 + ["2班"] * 4,
"姓名": ["张三", "张三", "李四", "李四"] * 2,
"科目": ["语文", "数学"] * 4,
"成绩": [80, 75, 90, 85, 70, 88, 92, 78],
})
pt2 = df3.pivot_table(
index=["班级", "姓名"],
columns="科目",
values="成绩",
aggfunc="mean",
)
print(pt2)
行变成”班级 + 姓名”两层。columns 同样能传列表,比如 columns=[“科目”, “学期”],列也变两层。透视后想取某个班级的数据,用 §22 的 xs 或 loc 按层级筛选即可。
多级透视的结果和 §37 的 groupby 有相似之处:都是按多个维度切分数据。区别在于 pivot_table 把分组维度拆到了行列两个方向,groupby 只往一个方向堆。
margins:加一行一列”合计”
想要 Excel 透视表那种”总计”行列,加 margins=True:
pt3 = df2.pivot_table(
index="姓名",
columns="科目",
values="成绩",
aggfunc="mean",
margins=True,
)
print(pt3)
结果右下角多出 All 行和 All 列,数值按 aggfunc 同一方式汇总(这里是全体平均)。标签名可以改:margins_name=“合计”。margins 的汇总口径和 aggfunc 一致,别指望它能算别的统计量。
Tippivot_table 还有 fill_value 参数,能把没数据的位置填成指定值(比如 0),适合报表输出。它只影响展示,不影响聚合计算。
pivot 与 pivot_table 怎么选
一句话:pivot 是”重排”,pivot_table 是”重排 + 聚合”。选型看三点:
- 组合是否唯一——唯一用 pivot,有重复用 pivot_table;
- 要不要算总和、平均——要算就用 pivot_table;
- 要不要边际汇总——只有 pivot_table 支持 margins。
透视结果想还原成长表,用上一章的 melt:pt.reset_index().melt(id_vars=“姓名”)。长宽互转、透视、堆叠这套工具配合起来,能应付绝大多数表格整形需求。
小结
pivot 是纯重排,要求每个组合唯一;pivot_table 是”重排 + 聚合”,aggfunc 决定重复组合怎么合并。index、columns 传列表得到多级透视,margins=True 加总计行列。一句话选型:组合有重复、要算汇总,就用 pivot_table,否则 pivot 更简单。