首页 / Pandas 入门教程 / 透视表:pivot 与 pivot_table

Pandas 入门教程

透视表:pivot 与 pivot_table

本教程共 54 篇 · 第 30 篇 · 更新于 2026-08-11 · 约 6 分钟阅读

PandasPandas 入门教程pivotpivot_table透视表aggfunc

本节目标:会用 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 的三个参数就是变形的三要素:

  1. index:转成新表行索引的列;
  2. columns:转成新表列名的列,它的唯一值成为新列名;
  3. 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 一致,别指望它能算别的统计量。

Tip

pivot_table 还有 fill_value 参数,能把没数据的位置填成指定值(比如 0),适合报表输出。它只影响展示,不影响聚合计算。

pivot 与 pivot_table 怎么选

一句话:pivot 是”重排”,pivot_table 是”重排 + 聚合”。选型看三点:

  1. 组合是否唯一——唯一用 pivot,有重复用 pivot_table;
  2. 要不要算总和、平均——要算就用 pivot_table;
  3. 要不要边际汇总——只有 pivot_table 支持 margins。

透视结果想还原成长表,用上一章的 melt:pt.reset_index().melt(id_vars=“姓名”)。长宽互转、透视、堆叠这套工具配合起来,能应付绝大多数表格整形需求。

小结

pivot 是纯重排,要求每个组合唯一;pivot_table 是”重排 + 聚合”,aggfunc 决定重复组合怎么合并。index、columns 传列表得到多级透视,margins=True 加总计行列。一句话选型:组合有重复、要算汇总,就用 pivot_table,否则 pivot 更简单。