unstack-重塑(reshape)

simon
5
2025-10-14

unstack() 是 pandas 中用于 重塑(reshape) 数据的一个重要函数,主要用于将 MultiIndex(层次化索引)的 Series 或 DataFrame 中的 内层索引(或指定层级)“旋转”为列,从而将“长格式”数据转换为“宽格式”。

📌 基本功能

对一个具有 多层索引(MultiIndex) 的对象(通常是 SeriesDataFrame),unstack() 会:

  • 默认将最内层(最后一层)的索引 转换为新的 列(columns)

  • 原来的外层索引保留为 行索引(index)

  • 如果某些组合不存在,结果中会填充 NaN(可用 fill_value 参数替换)

✅ 示例说明

假设你有如下数据:

import pandas as pd

data = pd.DataFrame({
    'store': ['A', 'A', 'B', 'B', 'A'],
    'product': ['X', 'Y', 'X', 'Z', 'X'],
    'sales': [10, 20, 30, 40, 50]
})

执行分组计数:

s = data.groupby('store')['product'].value_counts()
print(s)

输出是一个 MultiIndex Series

store  product
A      X          2
       Y          1
B      X          1
       Z          1
Name: product, dtype: int64

现在使用 unstack()

df_wide = s.unstack(fill_value=0)
print(df_wide)

结果:

product  X  Y  Z
store         
A        2  1  0
B        1  0  1
  • 行索引:store(外层索引)

  • 列索引:product(原内层索引)

  • 值:计数

  • fill_value=0 将缺失值(如 B-Y、A-Z)设为 0


🔧 常用参数

参数

说明

level=-1

指定要“unstack”的索引层级(默认是最内层,即 -1)

fill_value=None

用于填充因缺失组合产生的 NaN(如 fill_value=0

你也可以指定层级名称或编号:

s.unstack(level='product')  # 如果索引有名字
s.unstack(level=1)          # 如果 product 是第1层(0-based)

💡 与 pivot / pivot_table 的区别

  • unstack() 作用于 已有 MultiIndex 的对象

  • pivot() 作用于 普通 DataFrame,需要指定 index, columns, values

  • unstack() 更适合在 groupby + value_counts() / size() 等操作后使用

unstack()pivot_table() 都是 pandas 中用于 重塑数据(reshape) 的函数,都能将“长格式”数据转换为“宽格式”,但它们在 使用前提、灵活性、功能定位 上有本质区别。

🔍 核心区别总结

特性

unstack()

pivot_table()

输入要求

必须是 MultiIndex(层次化索引) 的 Series 或 DataFrame

普通(扁平索引)DataFrame 即可

主要用途

将 MultiIndex 的某一层“从行转列”

通用的数据透视(支持聚合、缺失处理、多值列等)

是否支持聚合

❌ 不支持(数据必须已聚合好)

✅ 支持(可指定 aggfunc,如 sum, mean, count 等)

处理重复键

❌ 会报错(MultiIndex 不能有重复组合)

✅ 自动聚合重复的 (index, columns) 组合

灵活性

低(仅重塑结构)

高(支持 fill_value、margins、多值列、多索引等)

典型场景

groupby().value_counts().unstack()

从原始数据直接透视汇总


🧪 举例对比

场景:统计每个门店每种产品的销售次数

原始数据:

import pandas as pd

df = pd.DataFrame({
    'store': ['A', 'A', 'B', 'A', 'B'],
    'product': ['X', 'Y', 'X', 'X', 'Z'],
    'sales': [10, 20, 30, 40, 50]
})

✅ 使用 unstack()(需先聚合)
# 先 groupby 得到 MultiIndex Series
s = df.groupby(['store', 'product']).size()  # 或 value_counts()
print(s)
# store  product
# A      X          2
#        Y          1
# B      X          1
#        Z          1

# 再 unstack
result1 = s.unstack(fill_value=0)

⚠️ 注意:unstack() 不能直接作用于原始 df,必须已有 MultiIndex。


✅ 使用 pivot_table()(直接从原始数据透视)
result2 = pd.pivot_table(
    df,
    index='store',
    columns='product',
    values='sales',          # 可选:若不指定 values,会用所有数值列
    aggfunc='count',         # 聚合函数:统计次数
    fill_value=0
)

或者统计销售额总和:

result_sum = pd.pivot_table(df, index='store', columns='product', values='sales', aggfunc='sum', fill_value=0)

pivot_table() 可直接处理原始数据,并自动聚合重复项。


❗ 关键差异点详解

1. 对重复键的处理
  • unstack():要求 (index_level_0, index_level_1) 唯一,否则会报错(如 ValueError: Index contains duplicate entries)。

  • pivot_table()专门设计用于处理重复键,通过 aggfunc 自动聚合。

2. 是否需要预聚合
  • unstack()必须先通过 groupbyvalue_counts 等得到聚合后的 MultiIndex 数据

  • pivot_table()一步到位,聚合 + 透视同时完成。

3. 功能范围
  • unstack()纯结构变换工具(reshape only)。

  • pivot_table()数据分析工具(reshape + aggregate + summarize)。


✅ 如何选择?

你的需求

推荐方法

已有 groupby().size()value_counts() 结果,只需转宽表

unstack()

从原始数据直接做透视汇总(如求和、平均、计数)

pivot_table()

数据中存在重复的 (行, 列) 组合

pivot_table()

追求代码简洁、高效(已聚合好)

unstack()


💡 补充:pivot() vs pivot_table()

  • pivot():类似 unstack(),但作用于普通 DataFrame,不支持聚合,遇到重复键会报错。

  • pivot_table():是 pivot() 的增强版,支持聚合,更健壮。


总结一句话:

unstack() 是“结构转换器”,pivot_table() 是“数据透视分析器”。前者用于已聚合的 MultiIndex 数据重塑,后者用于从原始数据直接透视汇总。

动物装饰