一句话总结

stack() 将列索引旋转为行索引(宽表变长表),unstack() 将行索引旋转为列索引(长表变宽表)。两者是处理 MultiIndex 时最常用的重塑工具。

1. stack() — 堆叠(列 → 行)

将 DataFrame 的列索引旋转为最内层行索引,返回 Series 或 DataFrame。

基本语法

DataFrame.stack(level=-1, dropna=_NoDefault.no_default, sort=_NoDefault.no_default, future_stack=False)
参数说明
level指定要堆叠的列索引层级。默认为 -1(最内层);可传 int、str 或列表
dropna是否删除结果为 NaN 的行。默认为 True(v2.0 起由 future_stack 控制)
sort是否对结果索引排序,默认 False

示例:基本堆叠

import pandas as pd
import numpy as np
 
df = pd.DataFrame({
    'A': [1, 2],
    'B': [3, 4]
}, index=['x', 'y'])
 
stacked = df.stack()
print(stacked)
# x  A    1
#    B    3
# y  A    2
#    B    4
# dtype: int64

示例:多层列索引

cols = pd.MultiIndex.from_tuples([('class1', 'A'), ('class1', 'B'),
                                  ('class2', 'A'), ('class2', 'B')])
df_multi = pd.DataFrame(np.random.rand(3, 4), columns=cols, index=['r1', 'r2', 'r3'])
 
# 默认堆叠最内层('A'/'B')
df_multi.stack()
# 指定 level=0,堆叠 'class1'/'class2'
df_multi.stack(level=0)

示例:dropna 参数

df_with_na = pd.DataFrame({
    'A': [1.0, np.nan],
    'B': [3.0, 4.0]
}, index=['x', 'y'])
 
df_with_na.stack(dropna=True)   # 删除 NaN 行
df_with_na.stack(dropna=False)  # 保留 NaN 行

2. unstack() — 取消堆叠(行 → 列)

将行索引旋转为列索引,是 stack() 的逆操作。

基本语法

DataFrame.unstack(level=-1, fill_value=None, sort=True)
参数说明
level指定要取消堆叠的行索引层级。默认为 -1(最内层);可传 int、str 或列表
fill_value若结果中存在缺失值,用该值填充(可用于处理多重索引组合无数据的情况)
sort是否对结果列索引排序,默认 True

示例:基本取消堆叠

s = pd.Series([1, 2, 3, 4], index=pd.MultiIndex.from_product([['x', 'y'], ['A', 'B']]))
print(s.unstack())
#    A  B
# x  1  2
# y  3  4

示例:fill_value 参数

idx = pd.MultiIndex.from_tuples([('x', 'A'), ('x', 'B'), ('y', 'B')])
s = pd.Series([1, 2, 3], index=idx)
 
s.unstack(fill_value=0)
#    A  B
# x  1  2
# y  0  3   ← 缺失的 ('y', 'A') 被填充为 0

3. 多层索引的灵活操作

堆叠 / 取消堆叠多层索引的部分层级

df = pd.DataFrame(np.arange(8).reshape(2, 4),
                  index=pd.MultiIndex.from_product([['a', 'b'], ['one', 'two']]),
                  columns=pd.MultiIndex.from_product([['x', 'y'], ['i', 'j']]))
 
# 取消最内层行索引
df.unstack()
# 取消指定层(如第 0 层)
df.unstack(level=0)
# 同时取消多层
df.unstack(level=[0, 1])

多层索引堆叠后再恢复

stacked = df.stack()          # 列 → 行
restored = stacked.unstack()   # 行 → 列(逆操作)

使用 level 参数指定具体层级

# 堆叠 'class1'/'class2' 这一层(level=0)
df_multi.stack(level=0)
 
# 取消堆叠指定名称的行索引层
df_unstacked = df.unstack(level='color')

4. 用例:长宽格式互转

# 宽表 → 长表(stack)
wide = pd.DataFrame({'2023': [100, 200], '2024': [150, 250]}, index=['A', 'B'])
long_df = wide.stack().reset_index(name='value')
long_df.columns = ['item', 'year', 'value']
 
# 长表 → 宽表(unstack)
wide_again = long_df.set_index(['item', 'year'])['value'].unstack()

5. 注意事项

易混淆点

  • stack() 返回 Series(若所有列堆叠后仅剩一个值列);unstack() 通常返回 DataFrame。
  • stack() 默认 dropna=True,会静默删除缺失值;若需保留应设置 dropna=False。
  • 对于无 MultiIndex 的普通 DataFrame,unstack() 会将普通行索引转为列索引。

记忆技巧

  • Stack(堆叠):像把盘子叠起来,列 → 行,数据变”瘦高”。
  • Unstack(取消堆叠):把叠起来的盘子摊开,行 → 列,数据变”矮胖”。

相关笔记

  • 15.3 长宽转换:melt() / wide_to_long() 也可实现长宽转换,但更适用于列名本身是变量的场景
  • index:返回章节总览