多级excel表头需先用header=none读取并前向填充还原层级,再构建multiindex列索引,访问时必须用元组如('2024年q1','销售额'),清洗导出均需适配多级结构,否则易报keyerror或valueerror。

多级Excel表头(如合并单元格形成的行列标题)无法被 pandas.read_excel 直接解析为标准二维结构,必须先“压平”或重建索引,否则后续清洗会因列名丢失、数据错位而失败。
识别并还原被合并的表头行
Excel中常见的多级表头往往表现为前几行存在跨列合并单元格(例如第0行是“销售数据”,第1行是“2024年Q1”“2024年Q2”,第2行才是具体指标“销售额”“订单量”)。pandas.read_excel 默认会把合并单元格读作空值或重复值,导致列名断裂。
- 用
header=None读取全部原始行,保留所有表头信息 - 手动指定表头所在行范围,例如
skiprows=0, nrows=3提取前三行作为原始表头矩阵 - 对这三行做前向填充(
ffill):按列向下填充空值,还原逻辑层级。例如第1行某列为NaN,但上方第0行有值,则继承该值 - 拼接各层级形成复合列名:用元组
(level0, level1, level2)或字符串"2024年Q1_销售额",避免重名
用 MultiIndex.from_arrays 构建多级列索引
直接用列表推导拼接列名容易出错,尤其当某列在某一级为空时。更稳妥的方式是显式构造 MultiIndex。
- 先用
df.iloc[0:3]提取表头行,再用.fillna(method='ffill')按列填充 - 转置后对每列调用
.tolist(),得到三个长度一致的层级列表:level0_list、level1_list、level2_list - 传入
pd.MultiIndex.from_arrays([level0_list, level1_list, level2_list])生成列索引 - 注意:若某列在 level0 是空,但 level1 有值,填充后可能变成 “Unnamed: 0” —— 需提前用
.replace(r'^Unnamed.*$', '', regex=True)清洗
跳过表头、读取数据体并绑定列索引
表头和数据体通常不连续(中间可能夹着空行或说明文字),硬编码 skiprows 容易错位。
- 先用
pd.read_excel(file, header=None)全量读取,用df.dropna(how='all')去掉全空行 - 定位数据起始行:找第一个非空且非表头模式的行(例如检查该行是否全是数字/日期/字符串混合,而非全为文本)
- 用
df.iloc[data_start_row:]切片获取数据体,并重设列名为之前构建的MultiIndex - 务必设置
dtype=str或逐列指定类型,避免 pandas 自动将“123”识别为 int 导致后续字符串操作(如.str.contains)报错
清洗时避开 MultiIndex 的常见陷阱
有了多级列名后,df['销售额'] 会报 KeyError,因为列不再是扁平字符串,而是元组。很多清洗操作会意外失效。
- 访问单列必须用元组:例如
df[('2024年Q1', '销售额')],或用df.xs('销售额', level=1, axis=1)提取 level1=“销售额”的所有列 -
df.fillna()对多级列默认生效,但df.dropna(subset=['销售额'])会失败——必须写成df.dropna(subset=[('2024年Q1', '销售额')]) - 重命名列别用
df.rename(columns={'旧': '新'}),应改用df.columns = df.columns.set_levels(...)或遍历df.columns.map() - 导出时若想还原为普通Excel表头(非多级),需先调用
df.columns = ['_'.join(col).strip() for col in df.columns.values]扁平化
最麻烦的不是读取,而是后续所有清洗动作都要适配 MultiIndex 的访问语法;一旦漏掉一层括号或写错 level,错误信息往往不提示真实原因,只报 KeyError 或 ValueError: cannot handle a non-unique multi-index! —— 这时候得回头检查表头填充是否彻底、有没有未处理的空层级。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











