本文介绍如何使用 pandas 的 melt() 方法将包含重复模式列(如 degree1/degree2…、specialisation1/specialisation2…)的宽格式员工教育数据,规范转换为每行一条学历记录的长格式结构,便于后续逐人逐学历处理(如 Selenium 自动化填报)。
本文介绍如何使用 pandas 的 `melt()` 方法将包含重复模式列(如 degree1/degree2…、specialisation1/specialisation2…)的宽格式员工教育数据,规范转换为每行一条学历记录的长格式结构,便于后续逐人逐学历处理(如 selenium 自动化填报)。
在处理招聘或人事类 Excel 数据时,常遇到“一人多学历”的建模困境:为兼容最多 5 项教育背景,表格设计采用宽表结构(degree1, specialisation1, college1, degree2, …, college5),这虽便于人工填写,却严重阻碍程序化处理——直接遍历 df.iterrows() 后仍需手动解析字段编号、拼接逻辑、容错缺失值,代码冗长且易出错。
更专业、可维护的解法是数据规范化(Normalization):将宽表重塑为符合第三范式的长表结构,即每行唯一标识一位候选人的一条教育经历。核心工具是 pandas 的 pd.melt() —— 它能批量“拆解”具有命名规律的列,生成结构清晰、语义明确的扁平化数据。
✅ 正确实现步骤
假设已读取原始 Excel:
import pandas as pd
df = pd.read_excel("applicants.xlsx")原始列包括固定属性(sr_no, name, address, mobile, emp_status)和重复教育字段(degree1–degree5, specialisation1–specialisation5, college1–college5)。我们首先用 melt() 展开所有教育相关列:
# 定义不变的标识列(id_vars)
id_cols = ['sr_no', 'old_emp_id', 'name', 'address', 'mobile', 'emp_status']
# 使用 melt 展开所有以 'degree', 'specialisation', 'college' 开头的列
df_melted = pd.melt(
df,
id_vars=id_cols,
var_name='attribute',
value_name='value'
)此时 df_melted 每行含完整候选人信息 + 一个属性名(如 'degree1')及其值。下一步是提取学位序号与字段类型,并重构为规整的教育记录:
# 提取序号(如 'degree1' → 1, 'college3' → 3)和字段类型('degree', 'specialisation', 'college')
df_melted[['field_type', 'seq']] = df_melted['attribute'].str.extract(r'^(degree|specialisation|college)(\d+)$')
df_melted['seq'] = df_melted['seq'].astype(int)
# 过滤掉无效行(如空字段名或未匹配的列)
df_melted = df_melted.dropna(subset=['field_type', 'seq'])
# 透视:将同一人的同一次序(seq)的 degree/specialisation/college 聚合成一行
df_education = df_melted.pivot_table(
index=id_cols + ['seq'],
columns='field_type',
values='value',
aggfunc='first' # 防止重复索引导致聚合异常
).reset_index()
# 清理列名:移除多级索引,重命名
df_education.columns.name = None
df_education = df_education.rename(columns={
'degree': 'degree',
'specialisation': 'specialisation',
'college': 'college'
})
# 可选:按候选人和序号排序,提升可读性
df_education = df_education.sort_values(['sr_no', 'seq']).reset_index(drop=True)最终得到标准长表:
| sr_no | old_emp_id | name | address | mobile | emp_status | seq | degree | specialisation | college |
|---|---|---|---|---|---|---|---|---|---|
| 1 | 24 | Amit | ABC Road | 356363474 | full-time | 1 | Computer Science | Robotics | IIT Delhi |
| 1 | 24 | Amit | ABC Road | 356363474 | full-time | 2 | MSC ML | MIT | — |
| 1 | 24 | Amit | ABC Road | 356363474 | full-time | 3 | PHD AI | Harvard | — |
? 关键优势:此后可直接 for _, edu_row in df_education.iterrows(): 安全遍历每条学历;配合 groupby('sr_no') 还能轻松实现“每人一组学历”的嵌套处理,完美适配 Selenium 表单自动化中「先填基本信息 → 循环添加教育经历」的业务流。
⚠️ 注意事项与最佳实践
- 字段命名一致性至关重要:melt() 依赖列名规律。若实际数据中存在 specialization2(美式拼写)与 specialisation1(英式),需预处理统一(如 df.columns = df.columns.str.replace('specialization', 'specialisation'))。
- 缺失值处理:pivot_table(..., aggfunc='first') 自动跳过 NaN,但建议在 melt 后检查 df_melted['value'].isna().sum(),确认空学历字段是否合理(如 degree3 为空表示仅两项学历)。
- 性能提示:对超大数据集(>10 万行),避免在循环中调用 df.iloc 或多次 filter;优先使用向量化操作(如 map、merge)完成字段映射。
- 扩展性设计:若未来增加 course_start_date1 等新字段,只需将其加入 melt 的隐式覆盖范围(不需修改逻辑),天然支持横向扩展。
通过这一范式,你不再需要手写 for j in range(1, 6) 的脆弱循环,而是用声明式数据操作构建健壮、可测试、易协作的数据流水线——这才是 Pandas 真正擅长的领域。










