数据分析中,面对原始流水数据,需要按多个维度分组汇总出二维报表。Pandas 的 pivot_table 能将分组、聚合、重塑三步合并为一行代码,是生成报表的最直接工具。本文从参数详解、基础示例、真实场景、横向对比到综合实战,完整演示 pivot_table 的核心用法。
核心参数说明
pivot_table 的签名为:
- pandas.pivot_table(
- data,
- values=None,
- index=None,
- columns=None,
- aggfunc='mean',
- fill_value=None,
- margins=False,
- dropna=True,
- margins_name='All',
- observed=False,
- sort=True
- )
复制代码
每个参数的含义和注意事项:
- data:DataFrame,必填。
- values:str 或 list,要聚合的数值列;省略则对所有数值列聚合。
- index:str 或 list,行分组字段,传列表形成多层行索引。
- columns:str 或 list,列分组字段,传列表形成多层列索引。
- aggfunc:func / list / dict,默认 'mean',支持字符串、函数、列表、字典,字典语法可对不同列使用不同聚合函数。
- fill_value:标量,聚合后产生的 NaN 用此值填充。
- margins:bool,是否添加行列汇总(小计/总计)。
- dropna:bool,聚合前丢弃全为 NaN 的列。注意与 fill_value 顺序:先丢后填。
- margins_name:str,汇总行/列标签,可改为“合计”或“总计”。
- observed:bool,仅对 Categorical 类型有效,建议 pandas 2.2+ 设为 True 只显示实际出现的组合,避免产生全零行。
- sort:bool,结果是否排序(v1.3.0 新增)。
基础用法演示
先准备示例数据:
- import pandas as pd
- import numpy as np
- df = pd.DataFrame({
- "A": ["foo", "foo", "foo", "foo", "foo", "bar", "bar", "bar", "bar"],
- "B": ["one", "one", "one", "two", "two", "one", "one", "two", "two"],
- "C": ["small", "large", "large", "small", "small", "large", "small", "small", "large"],
- "D": [1, 2, 2, 3, 3, 4, 5, 6, 7],
- "E": [2, 4, 5, 5, 6, 6, 8, 9, 9]
- })
复制代码
最基础用法:按 A、B 分行,按 C 分列,对 D 列求和:
- table = pd.pivot_table(df, values='D', index=['A', 'B'],
- columns=['C'], aggfunc="sum", fill_value=0)
- print(table)
复制代码
输出中原本缺失的组合(如 foo/two/large)用 fill_value=0 填充为 0。
对不同列使用不同聚合函数:
- table = pd.pivot_table(df, values=['D', 'E'], index=['A', 'C'],
- aggfunc={'D': 'mean', 'E': ['min', 'max', 'mean']})
- print(table)
复制代码
结果列变为多层索引,E 列下挂三个子列。
真实场景实战
1. 销售报表
按季度和地区汇总销售额与利润:
- data = {
- '地区': ['华东', '华东', '华北', '华北', '华南', '华南'],
- '产品': ['A', 'B', 'A', 'B', 'A', 'B'],
- '季度': [1, 1, 1, 2, 2, 2],
- '销售额': [150000, 120000, 90000, 110000, 130000, 95000],
- '利润': [30000, 24000, 18000, 22000, 26000, 19000]
- }
- df_sales = pd.DataFrame(data)
- pivot = pd.pivot_table(
- data=df_sales,
- values=['销售额', '利润'],
- index='季度',
- columns='地区',
- aggfunc='sum',
- fill_value=0,
- margins=True,
- margins_name='总计'
- )
- print(pivot)
复制代码
b]2. 时间维度多层索引
将“地区”和“月份”作为行索引,产品作为列:
- df['月份'] = df['日期'].dt.month
- pivot = pd.pivot_table(
- data=df,
- index=['地区', '月份'],
- columns='产品',
- values='销售额',
- aggfunc='sum'
- )
- # 用 xs 切片
- print(pivot.xs('华东', level='地区'))
复制代码
3. 用户行为多函数聚合
同时查看总访问量和人均次数:
- pivot = pd.pivot_table(
- data=df_user,
- index='用户等级',
- columns='访问渠道',
- values='访问次数',
- aggfunc=['sum', 'mean'],
- fill_value=0
- )
复制代码
高级技巧
自定义聚合函数:用 lambda 计算极差(最大值减最小值):
- pivot = pd.pivot_table(df, values='D', index='A',
- aggfunc=lambda x: x.max() - x.min())
复制代码
同时使用多个内置函数:
- pivot = pd.pivot_table(df, values='D', index='A',
- aggfunc=['sum', 'mean', 'count', np.std])
复制代码
扁平化多层列名:当 aggfunc 为列表/字典时,列索引变为 MultiIndex,可用列表推导压平:
- pivot.columns = ['_'.join(col).strip() for col in pivot.columns.values]
复制代码
链式操作:透视结果是标准 DataFrame,可直接接 reset_index、sort_values、query:
- result = (
- pd.pivot_table(df, values='销售额', index='地区', aggfunc='sum')
- .reset_index()
- .sort_values('销售额', ascending=False)
- .query('销售额 > 100000')
- )
复制代码
相近方法对比
| 方法 | 支持重复值 | 支持聚合 | 输出格式 | 适用场景 |
|------|-----------|---------|---------|---------|
| pivot() | ❌ | ❌ | 宽格式 | 数据唯一,纯重塑 |
| pivot_table() | ✅ | ✅ 灵活 | 宽格式 | 汇总报表、多维分析 |
| groupby() | ✅ | ✅ 灵活 | 长格式 | 分组统计,输出灵活 |
| crosstab() | ✅ | ✅ 默认频次 | 宽格式 | 交叉频次统计 |
经验:生成“行×列”二维汇总统首选 pivot_table;仅单列分组用 groupby 更简洁;问卷交叉分析用 crosstab。
综合实战:电商订单分析
模拟 200 条订单数据,按地区+季度为行、产品类别为列,销售额求和、利润求均值,并添加总计:
- import pandas as pd
- import numpy as np
- np.random.seed(42)
- n = 200
- df = pd.DataFrame({
- '地区': np.random.choice(['华东', '华北', '华南', '西部'], n),
- '产品类别': np.random.choice(['电子', '服装', '食品'], n),
- '季度': np.random.choice([1, 2, 3, 4], n),
- '销售额': np.random.randint(1000, 50000, n),
- '利润': np.random.randint(100, 10000, n),
- '订单数': np.random.randint(1, 100, n)
- })
- pivot = pd.pivot_table(
- data=df,
- values=['销售额', '利润'],
- index=['地区', '季度'],
- columns='产品类别',
- aggfunc={'销售额': 'sum', '利润': 'mean'},
- fill_value=0,
- margins=True,
- margins_name='汇总'
- )
- # 扁平化列名
- pivot.columns = ['_'.join(col) for col in pivot.columns]
- print(pivot.head(10))
- # 单独看华东的数据
- print(pivot.xs('华东', level='地区'))
复制代码
总结
pivot_table 将分组、聚合、重塑压缩为声明式代码。核心习惯:用 index/columns 控制维度,aggfunc 字典处理异构聚合,margins=True 自动加总计,fill_value 填充空缺,结果是标准 DataFrame 可继续链式操作。多跑真实数据,很快就能掌握。 |