
本文介绍两种高效、简洁的 Pandas 方法,将具有两层列索引的 DataFrame 堆叠(stack)后,使原列索引 level=0 的标签成为结果行索引的最外层(而非默认的内层),避免手动调用 xs() 或循环拼接,适用于大规模数据(如 4 万行 × 64 列)。
本文介绍两种高效、简洁的 pandas 方法,将具有两层列索引的 dataframe 堆叠(stack)后,使原列索引 level=0 的标签成为结果行索引的最外层(而非默认的内层),避免手动调用 `xs()` 或循环拼接,适用于大规模数据(如 4 万行 × 64 列)。
在处理具有 MultiIndex 列的 DataFrame(例如分组指标 + 子指标结构)时,常需将其“透视”为长格式,且要求原始列的顶层标签(如 'A', 'B', 'C')直接作为新索引的第一级——这与 .stack(level=0) 的默认行为相反:后者会将 level=0 列标签压入索引的最内层,导致后续对齐或分组困难。
以下以一个可复现的小型示例为基础,展示两种推荐的 pandas 惯用写法:
import pandas as pd
import numpy as np
# 构造初始 DataFrame:3×3 列组合 → 9 列,2 层列索引
df_init = pd.DataFrame(
np.linspace(0, 89, 90, dtype='int').reshape(10, 9),
columns=pd.MultiIndex.from_product([['A', 'B', 'C'], ['a', 'b', 'c']])
)
✅ 方法一:两次 unstack()(推荐,语义清晰)
利用 unstack() 的逆向特性:先完全展开所有列索引(等价于 unstack([0,1])),再仅对 level=1(即原列索引的第二层)再次 unstack,可巧妙重排索引层级:
out = df_init.unstack().unstack(level=1)
该操作本质是:
-
df_init.unstack()→ 将全部列索引压入行索引,生成 3 级索引(原行索引 + level=0 + level=1); -
unstack(level=1)→ 将新索引的第 1 级(即原列 level=1,如'a'/'b'/'c')重新提为列,剩余两级(原行索引 + 原列 level=0)保留在行索引中,并自动按 level=0 排序 → 原列 level=0 成为索引最外层。
✅ 方法二:stack + swaplevel + sort_index
若偏好显式堆叠逻辑,可先按 level=0 堆叠,再交换索引层级并排序:
out2 = (df_init
.stack(level=0, future_stack=True) # 堆叠 level=0 列 → 新索引: (原行索引, level=1, level=0)
.swaplevel() # 交换最后两级 → (原行索引, level=0, level=1)
.sort_index(level=0)) # 按最外层(即原列 level=0)稳定排序
⚠️ 注意:swaplevel() 默认交换最后两级;sort_index(level=0) 不可省略,否则 'A', 'B', 'C' 可能交错出现,影响可读性与下游分组操作。
验证与性能提示
两种方法均严格等价于手动拼接 xs() 的结果:
assert all(x.equals(df_final) for x in [out, out2])
对于您提到的 40,000 行 × 64 列(16×4 多级列) 场景:
- 方法一(双
unstack)底层优化良好,内存局部性更优,通常更快; - 方法二逻辑更直观,但
swaplevel+sort_index引入额外开销,建议在列层级复杂时优先选方法一。
无论哪种方式,均避免了显式循环、多次 xs() 查找或 pd.concat(keys=...),真正符合 pandas 的向量化设计哲学。










