stack()和unstack()本质是multiindex层级的升降维操作,非简单行列交换;unstack默认提列最内层为新行索引末尾,stack同理降行最内层进列;真正交换行列索引需transpose()配合reorder_levels()。

stack() 和 unstack() 的本质是操作 MultiIndex 的层级顺序
它们不是“交换行列索引”,而是对 pd.DataFrame 的列(unstack)或行(stack)进行层级重塑:把某一层级从轴上“提出来”变成新维度,或“压进去”合并为单层。关键在 level 参数指定哪一层参与移动,以及结果中层级的默认顺序。
常见误解是以为 unstack() 总是把列变行——其实它默认作用于最内层列索引(level=-1),并将其“升维”为新的行索引层级;同理 stack() 默认把最内层行索引“降维”进列。
-
unstack()后的新行索引 = 原行索引 + 被 unstack 的列层级(按原顺序拼接) -
stack()后的新列索引 = 原列索引 + 被 stack 的行层级(按原顺序拼接) - 若想真正“交换”行/列索引结构(比如让原列索引整体变成新行索引,原行索引变成新列索引),需先
swaplevel()或reorder_levels()配合unstack()/stack(),而非单独调用
用 unstack() 把列索引某层“转成行索引”时,如何控制新层级位置
默认 unstack(level=-1) 会把最内层列索引追加到现有行索引末尾。若想让它成为新行索引的最外层,必须显式指定 level 并配合 swaplevel() 或直接用 reorder_levels()。
import pandas as pd
df = pd.DataFrame([[1,2],[3,4]],
index=pd.MultiIndex.from_tuples([('A', 'X'), ('B', 'Y')]),
columns=pd.MultiIndex.from_tuples([('cat', 'a'), ('dog', 'b')]))
# 想让列索引第一层 'cat/dog' 成为新行索引最外层
result = df.unstack(level=0).swaplevel(0, 1).sort_index()
# 注意:unstack(level=0) → 新行索引 = 原行索引 + 'cat/dog' 层(在末尾)
# swaplevel(0,1) → 把末尾那层提到最前
- 不指定
level时,unstack()默认处理列索引最内层(level=-1) - 若列索引有 3 层,
unstack(level=1)表示提取中间那层,其余两层保留在列上 -
unstack()后若有多层行索引,顺序是“原行索引各层 + 被 unstack 的列层”,不可逆序——必须靠swaplevel()或reorder_levels()调整
stack() 报错 “Index has duplicate keys” 的真实原因和解法
这不是数据重复问题,而是 stack() 尝试把行索引某层压进列时,导致列名冲突:多个(原行索引组合 + 列名)映射到同一个新列名。典型场景是原 DataFrame 列索引为单层,而行索引含多层,stack() 后新列名仅由被 stack 的行层级构成,丢失了区分度。
- 错误示例:
df.stack(level=0)对一个列是单层、行是MultiIndex的 DataFrame 执行,若不同行组有相同值的 level-0,就会生成重复列名 - 解法一:先确保被 stack 的行层级在组合中唯一,例如用
df.index.droplevel(...)精简后再 stack - 解法二:改用
df.reset_index().set_index([...]).stack()显式构造无歧义的列结构 - 更稳妥做法:避免直接 stack 多层行索引,优先用
melt()+set_index()组合替代
真正“交换行列索引结构”的最小可行步骤
如果目标是让原列索引完全替代原行索引的位置(即行列索引互换角色),stack 和 unstack 单独做不到,必须搭配 transpose() 和层级重排:
# 假设 df 是列、行均为 MultiIndex 的 DataFrame # 步骤:1. transpose → 行列索引互换但层级顺序不变 # 2. 对新行索引(原列索引)调用 reorder_levels() 按需排序 # 3. 对新列索引(原行索引)同样 reorder_levels() df_t = df.transpose() df_t.index = df_t.index.reorder_levels([1,0]) # 调整新行索引层级顺序 df_t.columns = df_t.columns.reorder_levels([1,0]) # 调整新列索引层级顺序
-
transpose()是唯一能真正交换行列索引角色的操作,stack/unstack只是升降维 - 层级顺序错位是高频问题:
transpose()不改变各层级内部顺序,只交换轴身份,所以常需后续reorder_levels() - 若原索引层级数不等(如行是 2 层、列是 3 层),
transpose()仍可执行,但结果中对应轴的层级数也会互换——这点容易被忽略
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











