
本文介绍如何将 pandas dataframe 中具有 multiindex 的列(如两级列索引)的第一级(level 0)提取为普通数据列,同时保留其余结构,适用于宽表转长表、特征归一化预处理等场景。
在实际数据分析中,我们常遇到列索引为 MultiIndex 的 DataFrame(例如由 pd.pivot_table 或嵌套字典构造生成),此时若想将某一级列名(如第一级)转化为普通数据列(即“列索引 → 数据列”),标准的 melt() 并不能直接满足需求——因为它默认作用于行索引和值,而非列层级。
正确思路是:先通过 stack() 将列索引层级“压入”行索引,再利用 reset_index() 提取所需层级为列。具体步骤如下:
- df.stack(0):将列索引的 level 0(即 'a', 'b', 'c')压入行索引最内层,得到一个 Series(索引为 (row_idx, level0, level1));
- .T:转置,使原行变为列、原列层级重组,便于下一步按 level 0 堆叠;
- .stack(0):再次堆叠,这次将 level 0(即 'a', 'b', 'c')作为新索引的第一层,m1/m2 作为第二层,数值对齐;
- .reset_index():将多级索引全部转为普通列;
- .drop('level_1', axis=1):删除冗余的原行索引列(即 level_1,对应原始行号),保留 level_0(即 'a', 'b', 'c')作为目标列。
完整代码如下:
result = (df
.stack(0) # 压入 level 0 到索引
.T
.stack(0) # 重构后再次堆叠,使 level 0 成为主索引层
.reset_index()
.drop('level_1', axis=1)
.rename(columns={'level_0': 'level0'})) # 可选:美化列名输出结果为:
level0 m1 m2 0 a 0 10 1 a 1 11 2 b 2 12 3 b 3 13 4 c 3 13 5 c 4 14
✅ 注意事项:
- 若原始 DataFrame 行索引非默认整数(如带时间戳或字符串),建议先 df.reset_index(drop=True) 避免混淆;
- stack(0) 中的 0 指定要堆叠的列索引层级(level 0),若需提取 level 1,则改用 stack(1) 并相应调整后续逻辑;
- 替代方案(更直观):使用 df.columns.to_frame().assign(value=df.values.ravel()).explode('value') 配合 pivot,但可读性与健壮性不如 stack + reset_index 组合。
该方法简洁高效,是处理 MultiIndex 列“降维”的推荐范式。










