Python 玩转 Excel 案例【第 16 期】
Python 玩转 Excel 案例【第 16 期】
📌 案例说明
原始数据表:
员工信息表:
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
工资表(匹配前):
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
这是日常数据处理中非常常见的场景:两张表需要根据共同字段(工号)进行信息匹配。工资表的姓名列为空,需要从员工信息表中把对应的姓名填充过来。
目标任务:
在 Excel 中,这类需求通常使用 VLOOKUP 函数解决。
本期案例将跟大家一起学习如何用 Python 实现类似 VLOOKUP 的功能。
核心操作:数据映射 + 数据质量核查
-
• 关键函数: map()、isin()、tolist() -
• 核心逻辑:用工号作为桥梁,从信息表匹配姓名,同时检查哪些工号匹配失败
📜 完整代码
"""
VLOOKUP匹配填充 - 根据工号从信息表匹配姓名
数据质量核查,输出未匹配到的工号列表
"""
import pandas as pd
# 读取两张表
df_info = pd.read_excel("员工信息表.xlsx") # 工号、姓名、部门
df_salary = pd.read_excel("工资表.xlsx") # 工号、姓名(空)、基本工资
# 根据工号匹配姓名(类似VLOOKUP)
df_salary["姓名"] = df_salary["工号"].map(df_info.set_index("工号")["姓名"])
# 数据质量核查:检查哪些工号没匹配上
unmatched = df_salary[~df_salary["工号"].isin(df_info["工号"])]["工号"].tolist()
print(f"⚠️ 未匹配到的工号:{unmatched}" if unmatched else "✅ 全部匹配成功")
# 保存结果
df_salary.to_excel("工资表_填充后.xlsx", index=False)
运行结果:
⚠️ 未匹配到的工号:[99999]
匹配后的工资表:
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
⚠️ 注意:工号 99999 在员工信息表中不存在,所以姓名填充为
NaN(空值),并被程序检测出来。
第一步:导入所需库
import pandas as pd
代码解释:
-
• import pandas as pd
导入 pandas 库,这是 Python 数据处理的核心武器。它提供了read_excel()读取 Excel、DataFrame数据结构、map()数据映射、isin()条件筛选等强大功能。
第二步:读取数据
df_info = pd.read_excel("员工信息表.xlsx") # 工号、姓名、部门
df_salary = pd.read_excel("工资表.xlsx") # 工号、姓名(空)、基本工资
代码解释:
-
• pd.read_excel()
读取 Excel 文件,返回 DataFrame(数据表格)对象。 -
• df_info
存储员工信息表,包含工号、姓名、部门三列。 -
• df_salary
存储工资表,包含工号、姓名(空)、基本工资三列。
💡 小贴士:
read_excel()默认读取第一个工作表,如果文件有多个 Sheet,可以用sheet_name参数指定,如pd.read_excel("文件.xlsx", sheet_name="Sheet2")
第三步:核心匹配(VLOOKUP)
df_salary["姓名"] = df_salary["工号"].map(df_info.set_index("工号")["姓名"])
这行代码是整期案例的灵魂,实现了类似 Excel VLOOKUP 的功能。我们来一步步拆解:
3.1 df_info.set_index("工号")
将信息表的”工号”列设为行索引。
执行前:
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
执行后:
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
3.2 df_info.set_index("工号")["姓名"] – 构建查找表
只取出”姓名”这一列,得到一个 Series(带标签的一维数组),本质上就是一个键值对映射:
|
|
|
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
为什么不能直接写 df_info["姓名"]?
|
|
|
|
|---|---|---|
df_info["姓名"] |
['张三','李四','王五'...] |
|
df_info.set_index("工号")["姓名"] |
|
|
3.3 .map() 执行匹配
map() 是 pandas 中用于数据映射的方法,将一个 Series 中的每个值按照规则转换成另一个值。
df_salary["工号"].map(查找表)
执行过程:
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3.4 赋值给姓名列
将匹配结果赋给 df_salary["姓名"],完成姓名填充。
完整流程示意图
原始 df_info:
┌─────────┬────────┬────────┬────────┐
│ 数字索引 │ 工号 │ 姓名 │ 部门 │
├─────────┼────────┼────────┼────────┤
│ 0 │ 10001 │ 张三 │ 销售部 │
│ 1 │ 10002 │ 李四 │ 技术部 │
│ 2 │ 10003 │ 王五 │ 技术部 │
│ 3 │ 10004 │ 赵六 │ 市场部 │
│ 4 │ 10005 │ 周七 │ 人事部 │
└─────────┴────────┴────────┴────────┘
↓
.set_index("工号")
↓
┌────────┬────────┬────────┐
│ 工号 │ 姓名 │ 部门 │ ← 工号变成了行索引
├────────┼────────┼────────┤
│ 10001 │ 张三 │ 销售部 │
│ 10002 │ 李四 │ 技术部 │
│ 10003 │ 王五 │ 技术部 │
│ 10004 │ 赵六 │ 市场部 │
│ 10005 │ 周七 │ 人事部 │
└────────┴────────┴────────┘
↓
["姓名"]
↓
┌────────┬────────┐
│ 工号 │ 姓名 │ ← 只保留姓名列(查找表)
├────────┼────────┤
│ 10001 │ 张三 │
│ 10002 │ 李四 │
│ 10003 │ 王五 │
│ 10004 │ 赵六 │
│ 10005 │ 周七 │
└────────┴────────┘
↓
map() 匹配
↓
工资表工号 10003 → 查找表[10003] → "王五"
对比:有查找表 vs 无查找表
# 有查找表(简洁高效)
姓名 = 查找表[10003] # 返回 "王五"
# 无查找表(又长又慢)
姓名 = df_info[df_info["工号"] == 10003]["姓名"].values[0] # 返回 "王五"
第四步:数据质量核查
unmatched = df_salary[~df_salary["工号"].isin(df_info["工号"])]["工号"].tolist()
print(f"⚠️ 未匹配到的工号:{unmatched}" if unmatched else "✅ 全部匹配成功")
这行代码是数据质量核查的关键,能够自动找出哪些工号匹配失败。
逐步拆解
4.1 df_salary["工号"].isin(df_info["工号"])
判断工资表的每个工号是否在信息表的工号列表中。
|
|
|
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4.2 ~ 取反
波浪线 ~ 是取反运算符,True 变 False,False 变 True。
|
|
|
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4.3 df_salary[~df_salary["工号"].isin(df_info["工号"])]
用布尔值筛选行,只保留为 True 的行(即匹配失败的行)。
结果:
|
|
|
|
|---|---|---|
|
|
|
|
4.4 ["工号"] 和 .tolist()
取出工号列,并转换为 Python 列表。
最终结果:unmatched = [99999]
输出效果
-
• 有未匹配的工号: ⚠️ 未匹配到的工号:[99999] -
• 全部匹配成功: ✅ 全部匹配成功
💡 小贴士:这个数据质量检查环节非常重要!在实际工作中,匹配失败通常意味着:信息表漏了某位员工、工资表工号录入错误、或者工号格式不一致(如文本型数字 vs 数值型数字)。及早发现这些问题,可以避免后续数据错误。
第五步:保存结果
df_salary.to_excel("工资表_填充后.xlsx", index=False)
print("✅ VLOOKUP匹配完成")
代码解释:
-
• to_excel()
将 DataFrame 保存为 Excel 文件。 -
• "工资表_填充后.xlsx"
输出文件名。使用新文件名可以保留原始数据,方便对比验证。 -
• index=False
不保存行索引。pandas 默认会给每行加一个数字索引(0,1,2…),通常我们不需要它出现在 Excel 中。
⚠️ 注意:这里保存为新文件,原始
工资表.xlsx不会被修改。如果需要覆盖原文件,可以改成df_salary.to_excel("工资表.xlsx", index=False),但建议保留原始数据。
📚 本期核心知识点
📍 知识点 1:map() 数据映射
df_salary["姓名"] = df_salary["工号"].map(查找表)
-
• 作用:将一个 Series 中的每个值,按照规则转换成另一个值 -
• 支持:字典映射、Series 映射、函数映射 -
• 特点:匹配失败返回 NaN
其他用法示例:
# 字典映射
df["性别代码"] = df["性别"].map({"男": 1, "女": 0})
# 函数映射
df["工号前缀"] = df["工号"].map(lambda x: f"EMP-{x}")
📍 知识点 2:isin() + ~ 查找不存在的值
unmatched = df_salary[~df_salary["工号"].isin(df_info["工号"])]["工号"].tolist()
记忆口诀:取反筛选不存在,取出列值转列表
|
|
|
|
|---|---|---|
.isin() |
|
|
~ |
|
|
df[布尔值] |
|
|
.tolist() |
|
|
📍 知识点 3:set_index() 设置行索引
df_info.set_index("工号")["姓名"]
-
• 作用:将指定列设置为行索引,方便快速查找 -
• 链式操作:设置索引后可以立即用 [列名]取出需要的列
📍 知识点 4:to_excel() 保存文件
df.to_excel("输出文件.xlsx", index=False)
|
|
|
|
|---|---|---|
index |
|
True
False |
sheet_name |
|
"Sheet1" |
columns |
|
|
🔄 本案例核心流程
① 读取两张表 → ② 构建查找表 → ③ map 匹配姓名 → ④ 数据质量核查 → ⑤ 保存结果
读取员工信息表 (df_info) 读取工资表 (df_salary)
↓ ↓
set_index("工号")["姓名"] 工号列
↓ ↓
查找表 ←─────────────────────────────────┘
↓
map() 匹配
↓
df_salary["姓名"] = 匹配结果
↓
数据质量核查(isin + ~ + tolist)
↓
保存到新Excel (to_excel)
📦 资源获取提示
关注「码农自习室」,后台回复关键词 PythonExcel案例,即可获取本文完整代码及配套练习数据集,一起动手掌握高效数据操作的核心技巧!
❤️ 支持我们
如果本文对你有帮助,欢迎点赞 + 关注 + 点亮小红心,你的支持就是我们持续创作的最大动力~
夜雨聆风