三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

Python实现Excel数据高效比对与清洗

Python实现Excel数据高效比对与清洗

1. 问题场景与需求分析

在日常HR管理或行政工作中,我们经常需要处理来自不同系统的员工数据。比如:

  • 考勤系统导出的当月在职人员名单
  • 财务系统提供的工资发放清单
  • 部门自行维护的项目组成员表

这些数据通常以Excel工作表形式存在,但往往存在以下痛点:

  1. 各系统间员工ID格式不统一(如有的带前缀,有的纯数字)
  2. 姓名可能存在简繁体/大小写差异
  3. 部分字段可能包含多余空格等隐形字符
  4. 需要直观展示比对结果供非技术人员查阅

实际案例:某公司年终审计时,发现考勤系统显示在职员工比HR系统多出12人,经查是离职员工未及时同步导致五险一金多缴,造成直接经济损失8万余元。

2. 技术方案选型

2.1 为什么选择Python

相比Excel自带函数或VBA,Python处理该任务的优势在于:

  • 处理大文件更高效(实测10万行数据,Python比VBA快3倍以上)
  • 更灵活的数据清洗能力(正则表达式、字符串处理等)
  • 丰富的可视化选项(条件格式、差异高亮等)
  • 可保存为模板脚本重复使用

2.2 核心工具栈

import pandas as pd # 数据操作 from openpyxl import load_workbook # Excel编辑 from openpyxl.styles import PatternFill # 单元格样式 import difflib # 模糊匹配

3. 完整实现步骤

3.1 数据预处理

def clean_data(df): # 统一字符串格式 df = df.apply(lambda x: x.str.strip() if x.dtype == "object" else x) # 处理空值 df.fillna('NULL_PLACEHOLDER', inplace=True) # 统一ID格式(示例:去除前缀) df['员工ID'] = df['员工ID'].str.replace('EMP-', '') return df # 读取两个工作表 df1 = pd.read_excel('data.xlsx', sheet_name='Sheet1') df2 = pd.read_excel('data.xlsx', sheet_name='Sheet2') # 清洗数据 df1_clean = clean_data(df1) df2_clean = clean_data(df2)

3.2 关键比对逻辑

3.2.1 精确匹配(推荐方案)
# 使用merge进行比对 result = pd.merge( df1_clean, df2_clean, on=['员工ID', '姓名'], # 关键字段 how='outer', indicator=True ) # 分类结果 matched = result[result['_merge'] == 'both'] only_in_df1 = result[result['_merge'] == 'left_only'] only_in_df2 = result[result['_merge'] == 'right_only']
3.2.2 模糊匹配(备选方案)

当姓名可能存在拼写差异时:

def fuzzy_match(row): # 使用difflib计算相似度 return difflib.SequenceMatcher( None, str(row['姓名_x']), str(row['姓名_y']) ).ratio() # 应用模糊匹配 fuzzy_results = result.apply(fuzzy_match, axis=1) result['相似度'] = fuzzy_results

3.3 可视化输出

def highlight_diff(sheet): # 设置差异高亮样式 red_fill = PatternFill(start_color='FFEE1111', end_color='FFEE1111', fill_type='solid') green_fill = PatternFill(start_color='FF11EE11', end_color='FF11EE11', fill_type='solid') # 遍历单元格标记差异 for row in sheet.iter_rows(): for cell in row: if 'DIFF_FLAG' in str(cell.value): cell.fill = red_fill elif 'NEW_FLAG' in str(cell.value): cell.fill = green_fill # 保存结果到新Excel with pd.ExcelWriter('comparison_result.xlsx') as writer: matched.to_excel(writer, sheet_name='匹配成功', index=False) only_in_df1.to_excel(writer, sheet_name='仅表1存在', index=False) only_in_df2.to_excel(writer, sheet_name='仅表2存在', index=False) # 应用高亮样式 wb = load_workbook('comparison_result.xlsx') for sheetname in wb.sheetnames: highlight_diff(wb[sheetname]) wb.save('comparison_result_final.xlsx')

4. 实战经验与避坑指南

4.1 性能优化技巧

大文件处理方案:

# 分块读取(适用于超大型文件) chunk_size = 10000 reader = pd.read_excel('large_file.xlsx', chunksize=chunk_size) for chunk in reader: process(chunk) # 自定义处理函数

内存优化参数:

pd.read_excel( 'data.xlsx', dtype={'员工ID': 'string', '部门': 'category'}, # 指定数据类型 usecols=['员工ID', '姓名', '部门'] # 只读取必要列 )

4.2 常见问题排查

问题1:编码错误导致乱码

# 指定编码格式(常见于包含中文的Excel) df = pd.read_excel('data.xlsx', engine='openpyxl', encoding='gbk')

问题2:日期格式不一致

# 统一日期格式 df['入职日期'] = pd.to_datetime(df['入职日期'], errors='coerce').dt.strftime('%Y-%m-%d')

问题3:隐藏字符干扰

# 彻底清洗不可见字符 import re df['姓名'] = df['姓名'].apply(lambda x: re.sub(r'[\x00-\x1F\x7F]', '', str(x)))

5. 扩展应用场景

5.1 多表联合比对

# 比对三个及以上工作表 from functools import reduce dfs = [df1, df2, df3] common_cols = list(reduce(lambda x, y: x.intersection(y), [set(df.columns) for df in dfs])) result = reduce(lambda left,right: pd.merge(left, right, on=common_cols, how='outer'), dfs)

5.2 自动化邮件报告

import smtplib from email.mime.multipart import MIMEMultipart from email.mime.base import MIMEBase from email import encoders msg = MIMEMultipart() msg['Subject'] = '员工数据比对报告' msg.attach(MIMEBase('application', 'octet-stream').set_payload(open('comparison_result_final.xlsx', 'rb').read())) encoders.encode_base64(msg.get_payload(0)) msg.get_payload(0).add_header('Content-Disposition', 'attachment', filename='result.xlsx') with smtplib.SMTP('smtp.example.com') as server: server.sendmail('sender@example.com', 'receiver@example.com', msg.as_string())

5.3 数据库集成方案

# 从数据库直接读取比对 import sqlalchemy engine = sqlalchemy.create_engine('postgresql://user:pass@localhost:5432/hr_db') df_db = pd.read_sql('SELECT * FROM employees', engine) df_excel = pd.read_excel('current.xlsx') pd.merge(df_db, df_excel, on='employee_id', how='outer')
← 返回列表