如何将带多级表头的Pandas DataFrame转换为规整的长格式数据

12次阅读

如何将带多级表头的Pandas DataFrame转换为规整的长格式数据

本文介绍一种高效、简洁的方法,利用multiindex和stack操作,将具有嵌套表头(年份+高低值)的原始dataframe重塑为结构清晰的长格式表格,适用于时间序列指标分析场景。

在实际数据分析中,我们常遇到以“复合列名”形式组织的宽格式数据——例如,同一指标(如low/high)按年份重复排列,且首两行分别承载语义层级(如year和level)。这类结构虽便于人工阅读,却不利于后续计算与建模。pandas 提供了强大的索引与重塑工具,可将其一键转为标准的长格式(tidy data),关键在于正确构建列的多级索引(MultiIndex)并合理使用 stack()

以下为完整实现流程:

import pandas as pd import numpy as np  # 构造原始数据(模拟典型“双层表头”结构) data = {     0: ['parameter', '', 'foo', 'bar'],     1: [2001, 'low', 1, np.nan],     2: [2001, 'high', 2, np.nan],     3: [2002, 'low', 7, 12],     4: [2002, 'high', 8, 13], } df = pd.DataFrame(data)  # 步骤1:提取列级语义 → 构建 MultiIndex # 取第0行(年份)和第1行(low/high)作为两级列名(跳过第0列,因其为行索引标识) years = df.iloc[0, 1:]   # [2001, 2001, 2002, 2002] levels = df.iloc[1, 1:]  # ['low', 'high', 'low', 'high'] idx = pd.MultiIndex.from_arrays([years, levels], names=('year', 'level'))  # 步骤2:清理数据主体 → 设置行索引 + 重命名列轴 df_clean = (df.iloc[2:]               # 跳过前两行(表头)             .set_index(0)             # 将第0列('foo', 'bar')设为行索引,命名为 'parameter'             .set_axis(idx, axis=1)    # 应用新构建的 MultiIndex 到列             .rename_axis('parameter') # 显式命名索引轴            )  # 步骤3:重塑 → stack 展开 level 维度,reset_index 标准化 result = df_clean.stack(level=0).reset_index() result.columns = ['parameter', 'year', 'level', 'value']  # 可选:重命名列提升可读性  # 若需进一步 pivot 成 'low'/'high' 为列(即目标输出格式),执行: final = result.pivot(index=['parameter', 'year'], columns='level', values='value')                 .reset_index()                 .rename_axis(None, axis=1)

运行后得到目标结构:

parameter  year  high  low 0       bar  2002  13.0 12.0 1       foo  2001   2.0  1.0 2       foo  2002   8.0  7.0

✅ 关键要点总结: MultiIndex.from_arrays() 是构建语义化列索引的核心,务必确保传入数组长度一致且顺序对应; set_axis(…, axis=1) 替代手动赋值 df.columns = …,更安全且支持链式调用; stack(0) 默认展开最内层列索引(即 ‘level’),若有多层需显式指定 level 参数; 最终 pivot() 可选——若原始需求是“每个 level 单独成列”,则必须补此步;若接受 level 作为变量列,则 stack().reset_index() 已足够(更符合 tidy data 原则)。

该方法鲁棒性强,可轻松扩展至更多层级(如增加地区、产品线等),是处理报表导出类非标准数据的推荐范式。

text=ZqhQzanResources