该用unstack而非pivot时:索引已含MultiIndex且需将某层转为列;pivot要求扁平数据并显式指定index/columns/values,误用于MultiIndex会报错。

什么时候该用 unstack 而不是 pivot
当你的索引(index)已经包含多层结构,且目标是把某一层“抬升为列”,unstack 是最直接的选择;而 pivot 要求原始数据是扁平的 DataFrame,必须显式指定 index、columns 和 values 三者。误用 pivot 去处理已有 MultiIndex 的数据,会报 ValueError: Index contains duplicate entries, cannot reshape。
常见场景:用 groupby(...).size() 或 pd.crosstab() 得到的带双层索引的结果,后续想按某个分类维度展开成列 —— 这时直接 .unstack(level=...) 即可,不用重设索引再 pivot。
-
unstack默认展开最内层索引(level=-1),也可指定整数或名称,例如.unstack('category') - 若展开后出现
NaN,说明原 MultiIndex 中存在缺失组合,这是正常行为,不是 bug - 展开后列名会变成
MultiIndex(如果原索引有多层),需要.columns = columns.droplevel(0)或.add_prefix()清理
stack 的典型误用:为什么结果多了一层括号
对一个普通 DataFrame 直接调用 .stack(),会把所有列压进最内层索引,生成一个 Series,其索引是 MultiIndex(原行索引 + 原列名)。如果你看到输出像 (0, 'A') 1.2 这种带括号的索引,不是错误,而是 stack 的标准行为 —— 它本就是为“列转行”设计的逆操作。
容易踩的坑:
立即学习“Python免费学习笔记(深入)”;
- 对已含
MultiIndex列的 DataFrame 用stack(),默认只压最内层列,可能不符合预期;需加level参数,如.stack(level=1) -
stack后若要还原回 DataFrame,不能直接unstack,得先确认索引层级是否匹配;否则会报ValueError: Index has duplicate keys - 数值列含
None或np.nan时,stack会自动丢弃这些项 —— 如果需要保留空位,得先fillna()
如何用 unstack 处理重复索引问题
当你调用 unstack 报错 Cannot unstack a duplicate axis,说明同一组索引键对应多个值,pandas 不知道该聚合还是报错。这不是数据问题,而是你没告诉它怎么处理。
解决路径只有两条:
- 先聚合再 unstack:例如
df.groupby(['user', 'item'])['score'].mean().unstack('item') - 用
set_index显式构造唯一 MultiIndex,例如df.set_index(['user', 'item', 'timestamp'], drop=False).unstack('item')(前提是 timestamp 能消除重复) - 强行去重(慎用):
df.drop_duplicates(['user', 'item']).set_index(['user', 'item']).unstack('item'),但会丢失信息
注意:unstack 本身不提供 aggfunc 参数,聚合必须在之前完成。
性能差异:为什么有时 pivot_table 比 unstack 快得多
当原始数据未设置 MultiIndex,且你需要按多个字段分组展开时,pivot_table(index=..., columns=..., values=..., aggfunc='first') 内部做了优化,避免了先 groupby 再 unstack 的两步开销。实测百万行数据下,pivot_table 可比等效的 groupby(...).first().unstack() 快 2–3 倍。
关键区别:
-
unstack是纯索引操作,不做计算;pivot_table是聚合+重塑一体化 -
unstack要求索引唯一,pivot_table天然支持重复键 + 聚合逻辑 - 如果只是简单“把一列变列名”,且数据已规整,
unstack更轻量;一旦涉及分组或缺失填充,pivot_table更稳
真正容易被忽略的是:unstack 后的列顺序默认按原索引中该 level 的唯一值排序,不是原始出现顺序 —— 如需保持顺序,得先用 pd.Categorical 固定列类别。


















