Pandas: 重塑与透视
最后更新:2026-08-26
同一个数据,"宽表"便于人类阅读,"长表"便于程序分析。Pandas 的 pivot / melt / stack / unstack 就是宽长互转的四大工具。本节用 Mermaid 图解 reshape 方向,让你看清"融化"和"旋转"的直觉含义——这是所有竞品教程都缺少的可视化。
⚠️ 注意: 以下代码需在本地 Python 环境中运行。
1. 你将学到
- ❶ pivot 透视
- ❷ pivot_table 聚合透视
- ❸ melt 宽→长
- ❹ stack / unstack
- ❺ 宽表 vs 长表与 Tidy Data
2. Alice 的销售报表转换
(1) 痛点:报表格式≠分析格式
Alice 的月度销售报表是宽表(月份横向展开),但做分析需要长表(月份作为一列):
PYTHON
import pandas as pd
# Wide format — easy to read, hard to analyze
wide = pd.DataFrame({
'product': ['Latte', 'Mocha', 'Americano'],
'Jan': [320, 280, 200],
'Feb': [350, 300, 220],
'Mar': [380, 310, 240]
})
print(wide)
# product Jan Feb Mar
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
(2) 解法:melt 融化为长表
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
:melt 宽→长(难度⭐)
PYTHON
import pandas as pd
wide = pd.DataFrame({
'product': ['Latte', 'Mocha', 'Americano'],
'Jan': [320, 280, 200],
'Feb': [350, 300, 220],
'Mar': [380, 310, 240]
})
# Melt: month columns become rows
long = wide.melt(id_vars='product', var_name='month', value_name='sales')
print(long)
# product month sales
# 0 Latte Jan 320
# 1 Mocha Jan 280
# 2 Americano Jan 200
# 3 Latte Feb 350
# ...
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
3. 宽表 vs 长表
(1) Mermaid 图解宽⇄长
graph LR
subgraph Wide["宽表 — 人读友好"]
W["product | Jan | Feb | Mar<br>Latte | 320 | 350 | 380<br>Mocha | 280 | 300 | 310"]
end
subgraph Long["长表 — 机器分析友好"]
L["product | month | sales<br>Latte | Jan | 320<br>Latte | Feb | 350<br>Latte | Mar | 380<br>Mocha | Jan | 280<br>..."]
end
Wide -->|"melt()"| Long
Long -->|"pivot()"| Wide
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
(2) 宽表 vs 长表对比
| 特性 | 宽表 | 长表 |
|---|---|---|
| 值的位置 | 列名中 | 行中 |
| 适合 | 人类阅读/报表 | 机器分析/ggplot |
| 新增月份 | 加列(结构变) | 加行(结构不变) |
| groupby | 不方便 | 天然支持 |
| 典型 | Excel 报表 | 数据库/Tidy Data |
4. pivot 透视
(1) 基础透视
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
:pivot 长→宽(难度⭐)
PYTHON
import pandas as pd
long = pd.DataFrame({
'product': ['Latte', 'Latte', 'Latte', 'Mocha', 'Mocha', 'Mocha'],
'month': ['Jan', 'Feb', 'Mar', 'Jan', 'Feb', 'Mar'],
'sales': [320, 350, 380, 280, 300, 310]
})
# Pivot: unique row=product, unique column=month, values=sales
wide = long.pivot(index='product', columns='month', values='sales')
print(wide)
# month Feb Jan Mar
# product
# Americano 220 200 240
# Latte 350 320 380
# Mocha 300 280 310
# Reset index to make product a regular column
wide = wide.reset_index()
print(wide.columns) # MultiIndex → flatten
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
⚠️ 注意: pivot 要求 index+columns 组合是唯一的。如果有重复(如同一产品同月多条记录),会报错。此时用 pivot_table(自动聚合)。
5. pivot_table 聚合透视
(1) 聚合透视
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
:pivot_table 聚合(难度⭐⭐)
PYTHON
import pandas as pd
# Multiple entries per product+month (need aggregation)
df = pd.DataFrame({
'product': ['Latte', 'Latte', 'Latte', 'Latte', 'Mocha', 'Mocha'],
'month': ['Jan', 'Jan', 'Feb', 'Feb', 'Jan', 'Feb'],
'sales': [150, 170, 180, 170, 140, 160],
'quantity': [30, 34, 36, 34, 28, 32]
})
# pivot_table with aggregation function
result = pd.pivot_table(
df,
values='sales',
index='product',
columns='month',
aggfunc='sum' # default is 'mean'
)
print(result)
# month Feb Jan
# product
# Latte 350 320
# Mocha 160 140
# Multiple aggregation functions
result2 = pd.pivot_table(
df,
values=['sales', 'quantity'],
index='product',
columns='month',
aggfunc={'sales': 'sum', 'quantity': 'mean'}
)
# Margins — add row/column totals
result3 = pd.pivot_table(
df, values='sales', index='product', columns='month',
aggfunc='sum', margins=True, margins_name='Total'
)
print(result3)
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
(2) pivot vs pivot_table
| 特性 | pivot | pivot_table |
|---|---|---|
| 重复值 | 报错 | 聚合 |
| 聚合函数 | 无 | sum/mean/count 等 |
| margins | 无 | ✅ 行列总计 |
| 多值列 | 单列 | 多列 |
| 适用 | 无重复的干净数据 | 有重复需聚合 |
6. melt 详解
(1) melt 参数
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
:melt 参数详解(难度⭐⭐)
PYTHON
import pandas as pd
df = pd.DataFrame({
'product': ['Latte', 'Mocha'],
'region': ['North', 'South'],
'Jan_sales': [320, 280],
'Feb_sales': [350, 300],
'Jan_quantity': [64, 56],
'Feb_quantity': [70, 60]
})
# Basic melt — all non-id columns become rows
result1 = df.melt(id_vars=['product', 'region'])
print(result1)
# Specify value_vars — only melt certain columns
result2 = df.melt(
id_vars=['product', 'region'],
value_vars=['Jan_sales', 'Feb_sales'],
var_name='month',
value_name='sales'
)
print(result2)
# Multiple id_vars
result3 = df.melt(
id_vars=['product'],
value_vars=['Jan_sales', 'Feb_sales'],
var_name='month_sales',
value_name='amount'
)
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
7. stack / unstack
(1) stack 列→行
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
:stack/unstack 与 MultiIndex(难度⭐⭐)
PYTHON
import pandas as pd
df = pd.DataFrame({
'Jan': [320, 280],
'Feb': [350, 300],
'Mar': [380, 310]
}, index=['Latte', 'Mocha'])
df.index.name = 'product'
# stack: columns → row level (wide → long)
stacked = df.stack()
print(stacked)
# product month
# Latte Jan 320
# Feb 350
# Mar 380
# Mocha Jan 280
# ...
# unstack: row level → columns (long → wide)
unstacked = stacked.unstack()
print(unstacked)
# Jan Feb Mar
# product
# Latte 320 350 380
# Mocha 280 300 310
# With MultiIndex: choose which level to unstack
multi = pd.DataFrame({
'sales': [320, 350, 280, 300],
'region': ['North', 'North', 'South', 'South'],
'month': ['Jan', 'Feb', 'Jan', 'Feb']
})
pivot = multi.set_index(['region', 'month'])['sales']
print(pivot.unstack(level='month'))
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
(2) 4 种重塑方法对比
| 方法 | 方向 | 输入→输出 | 适用 |
|---|---|---|---|
| pivot | 长→宽 | 指定 index/columns/values | 无重复长表 |
| pivot_table | 长→宽 | +aggfunc 聚合 | 有重复长表 |
| melt | 宽→长 | 指定 id_vars | 列名变行值 |
| stack | 列→行 | columns→index level | MultiIndex 操作 |
| unstack | 行→列 | index level→columns | stack 的逆操作 |
8. 完整示例:销售数据宽长转换全流程
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
:宽长转换全流程(难度⭐⭐⭐)
PYTHON
import pandas as pd
import numpy as np
# ============================================
# Comprehensive example: Wide ↔ Long
# Sales data reshape pipeline
# ============================================
# 1. Start with wide format (report-style)
np.random.seed(42)
wide = pd.DataFrame({
'product': ['Latte', 'Mocha', 'Americano', 'Espresso', 'Cappuccino'],
'region': ['North', 'South', 'East', 'West', 'North'],
'Jan': np.random.randint(200, 500, 5),
'Feb': np.random.randint(220, 520, 5),
'Mar': np.random.randint(240, 540, 5),
'Apr': np.random.randint(250, 550, 5)
})
print("=== Wide Format (5 products × 4 months) ===")
print(wide)
# 2. Melt to long format
long = wide.melt(
id_vars=['product', 'region'],
var_name='month',
value_name='sales'
)
print(f"\n=== Long Format: {len(long)} rows ===")
print(long.head(8))
# 3. Analyze in long format (groupby is natural)
monthly_total = long.groupby('month')['sales'].sum()
print(f"\n=== Monthly Totals ===\n{monthly_total}")
region_avg = long.groupby('region')['sales'].mean().round(0)
print(f"\n=== Region Avg ===\n{region_avg}")
# 4. Pivot back to wide (product × month summary)
summary_wide = pd.pivot_table(
long, values='sales', index='product', columns='month',
aggfunc='sum', margins=True, margins_name='Total'
)
print(f"\n=== Pivot Table Summary ===")
print(summary_wide)
# 5. Stack/unstack with MultiIndex
multi = long.set_index(['region', 'product', 'month'])['sales']
by_region = multi.unstack(level='month')
print(f"\n=== Unstack by Month (Region×Product) ===")
print(by_region.head(8))
TEXT
📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。
❓ 常见问题
Q pivot 和 pivot_table 区别?
A pivot 要求 index+columns 组合唯一,有重复就报错。pivot_table 遇到重复会聚合(默认 mean,可指定 sum/count 等),还支持 margins(行列总计)。简单无重复数据用 pivot,有重复或需要聚合用 pivot_table。日常 80% 场景用 pivot_table 更安全。
Q melt 的 id_vars 是什么?
A id_vars 是"不融化"的列——它们保留为行标识。其他所有列的列名变成一个变量列(var_name),值变成一个值列(value_name)。比如月份列 Jan/Feb/Mar 融化后,'Jan'/'Feb'/'Mar' 变成 month 列的值,对应数字变成 sales 列的值。
Q stack 和 unstack 互逆吗?
A 是的——stack 把列压进行(宽→长),unstack 把行展成列(长→宽)。
df.stack().unstack() 恢复原表。它们操作的是 MultiIndex 的层级——stack 增加行层级,unstack 增加列层级。level 参数控制操作哪一层。Q 什么时候用宽表/长表?
A 宽表适合人类阅读(报表/PPT/Excel),长表适合程序分析(groupby/merge/可视化)。Tidy Data 原则推荐长表:每列一个变量,每行一个观测。存储用长表(结构稳定),展示用宽表(pivot 输出)。
Q pivot 报重复值怎么办?
A 说明 index+columns 组合有重复行(如同一产品同月多条记录)。两个选择:① 用 pivot_table 加 aggfunc='sum'/'mean' 自动聚合;② 先 groupby 聚合再 pivot。pivot_table 更简洁,推荐优先。
Q melt 后怎么恢复?
A 用 pivot 或 pivot_table 恢复:
long.pivot(index='product', columns='month', values='sales')。melt + pivot 互为逆操作。注意 melt 后的数据类型可能变化(数值列变成 object),恢复前可能需要 astype 转换。Q Tidy Data 是什么?
A Tidy Data 是 Hadley Wickham 提出的数据整理原则:① 每个变量占一列;② 每个观测占一行;③ 每种观测类型占一个表。长表天然满足 Tidy Data,宽表不满足(月份变量藏在列名里)。Pandas 的 melt 就是为了把宽表"整理"成 Tidy Data。
📖 小节
- pivot 长→宽(无重复时),pivot_table 长→宽(有重复时聚合)
- melt 宽→长,id_vars 保留的列,其余列名→变量列,值→值列
- stack 列压进行(宽→长),unstack 行展成列(长→宽),互为逆操作
- 宽表人读友好,长表机器分析友好,Tidy Data 推荐长表
- 日常流程:原始数据→melt 整理→groupby 分析→pivot_table 报告
- pivot 遇重复报错→换 pivot_table + aggfunc
📝 作业
- 基础题(难度⭐):创建宽表(3 产品 × 3 月份),用 melt 转成长表,再用 pivot 转回宽表,验证结果一致。
- 进阶题(难度⭐⭐):创建含重复值的销售数据(同产品同月多条记录),用 pivot_table(aggfunc='sum') + margins=True 生成带行列总计的透视表。
- 挑战题(难度⭐⭐⭐):模拟 3 地区×4 产品×3 月份的长表数据,完成:set_index MultiIndex → unstack 按月份展宽 → stack 压回 → melt 另一种转换 → pivot_table 交叉分析。