Pandas: 重塑与透视

最后更新:2026-08-26

同一个数据,"宽表"便于人类阅读,"长表"便于程序分析。Pandas 的 pivot / melt / stack / unstack 就是宽长互转的四大工具。本节用 Mermaid 图解 reshape 方向,让你看清"融化"和"旋转"的直觉含义——这是所有竞品教程都缺少的可视化。

⚠️ 注意: 以下代码需在本地 Python 环境中运行。

1. 你将学到


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 图解宽⇄长

100%
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。

📖 小节


📝 作业

  1. 基础题(难度⭐):创建宽表(3 产品 × 3 月份),用 melt 转成长表,再用 pivot 转回宽表,验证结果一致。
  2. 进阶题(难度⭐⭐):创建含重复值的销售数据(同产品同月多条记录),用 pivot_table(aggfunc='sum') + margins=True 生成带行列总计的透视表。
  3. 挑战题(难度⭐⭐⭐):模拟 3 地区×4 产品×3 月份的长表数据,完成:set_index MultiIndex → unstack 按月份展宽 → stack 压回 → melt 另一种转换 → pivot_table 交叉分析。

← 上一课:拼接追加 · 下一课:字符串操作 →

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏