上周同事接了个活——公司要做年度数据分析,需要把各个部门报上来的 300 多张 Excel 表清洗干净。日期格式五花八门、姓名的姓和名之间有一堆空格、金额列里混着中文备注。他手动改了一下午,改到第五十张的时候放弃了。我路过看了一眼,写了几行代码,十分钟洗完。今天把这几行代码分享出来——你下次遇到同样的事,直接拿去用。
那天下午两点,隔壁部门的同事把文件夹发给我:「江湖救急,这些表格的数据能不能帮我统一一下?我一个人改到崩溃了。」
我点开看了几个文件。每个部门填表格的方式都不一样:
日期列:有人写「2024/3/5」,有人写「2024-03-05」,有人写「2024 年 3 月 5 日」 姓名列:有的姓和名之间有空格,有的没有,有的名字前面还带个部门简称 金额列:最离谱——有的写纯数字,有的写「¥1280」,有的写「1280 元(含税)」,还有一列金额被写成了文本,左上角一个小绿三角 手机号:有人写了区号,有人中间加了横杠「138-0000-1234」,有人什么符号都没加
三百张表格,每张几千行。手动一个一个改,别说一下午,一天都够呛。
环境准备
装好 Python(去 python.org 下载,勾选「Add Python to PATH」),然后装两个库:
pip install pandas openpyxl把所有表格文件扔到一个文件夹里,比如桌面新建一个 数据清洗 文件夹。
第一步:日期统一
这是最常见的问题——不同人填日期的方式不一样,Pandas 一行就能搞定。
import pandas as pddf = pd.read_excel("数据清洗/部门上报的表格.xlsx")# 不管原来长什么样,全转成 2024-03-05 这种格式df["日期"] = pd.to_datetime(df["日期"]).dt.strftime("%Y-%m-%d")df.to_excel("数据清洗/清洗完成.xlsx", index=False)pd.to_datetime() 这个函数很聪明——你给它「2024/3/5」「2024 年 3 月 5 日」「2024-03-05」,它都能认出来。然后用 .dt.strftime() 统一输出成你想要的格式。
但有一个坑:有些表格里日期列里混进了非日期数据——比如有人在日期那一格写了「待确认」。直接跑上面那行代码会报错。加个容错:
df["日期"] = pd.to_datetime(df["日期"], errors="coerce").dt.strftime("%Y-%m-%d")errors=「coerce」 的意思是:遇到认不出来的值,不报错,直接跳过,那一格变成空白。不会因为一个「待确认」让整个脚本停下来。
第二步:姓名去空格、去前缀
姓名列的问题分两种:多余的空格,和冗余的前缀。
去多余空格最简单——Pandas 里字符串列的 .str.strip() 方法两步搞定:先去掉首尾空格,再把中间多个连续空格压缩成一个。
# 去掉首尾空格 + 中间多个空格压成一个df["姓名"] = df["姓名"].str.strip().str.replace(r"\s+", " ", regex=True)去前缀稍微麻烦一点。我打开的那批文件里有人的名字前面写着「深圳-张三」「北京-李四」。前缀格式是「城市名-」,要把这部分去掉。
# 去掉"城市名-"前缀df["姓名"] = df["姓名"].str.replace(r"^[\u4e00-\u9fa5]+-", "", regex=True)这行代码的意思:从字符串开头找「一个或多个中文字符后面跟一个横杠」,找到了就删掉。「深圳-张三」变成「张三」,「北京-李四」变成「李四」。没有前缀的名字不受影响。
第三步:金额列去干扰字符
金额列的问题我遇到最多也最头疼——同一个表里金额那列可能是「1280」「¥1280」「1280 元」「1280 元(含税)」。中间混着中文字符和符号,Pandas 认不出它是数字。
解决思路:用正则把数字之外的东西全删掉,然后转成数字类型。
# 删掉所有非数字字符(保留小数点和负号)df["金额"] = df["金额"].astype(str).str.replace(r"[^\d.\-]", "", regex=True)# 转成数字df["金额"] = pd.to_numeric(df["金额"], errors="coerce")如果你要保留两位小数:
df["金额"] = df["金额"].round(2)第四步:手机号统一格式
手机号的问题不大,主要是中间有空格或者横杠——「138 0000 1234」或「138-0000-1234」。删掉这些分隔符就行。
# 去掉空格和横杠,只保留数字df["手机号"] = df["手机号"].astype(str).str.replace(r"\s|-", "", regex=True)如果有些号码前面加了区号「+86」或「86-」,顺手删掉:
df["手机号"] = df["手机号"].str.replace(r"^(\+?86[-\s]?)", "", regex=True)批量处理全部表格
上面四步是处理一张表的。你有 300 张表怎么办?跟上一篇合并的思路一样——遍历文件夹。
import pandas as pdimport globfiles = glob.glob("数据清洗/*.xls*")for f in files: df = pd.read_excel(f) # 1. 日期统一 df["日期"] = pd.to_datetime(df["日期"], errors="coerce").dt.strftime("%Y-%m-%d") # 2. 姓名清洗 df["姓名"] = df["姓名"].str.strip().str.replace(r"\s+", " ", regex=True) df["姓名"] = df["姓名"].str.replace(r"^[\u4e00-\u9fa5]+-", "", regex=True) # 3. 金额清洗 df["金额"] = df["金额"].astype(str).str.replace(r"[^\d.\-]", "", regex=True) df["金额"] = pd.to_numeric(df["金额"], errors="coerce").round(2) # 4. 手机号清洗 df["手机号"] = df["手机号"].astype(str).str.replace(r"\s|-", "", regex=True) df["手机号"] = df["手机号"].str.replace(r"^(\+?86[-\s]?)", "", regex=True) # 保存到新文件夹 df.to_excel(f"清洗完成/{f}", index=False)print(f"搞定!共清洗 {len(files)} 张表格")怎么运行: 把上面代码复制到记事本,保存为 clean_excel.py,放到你的文件夹旁边。终端 cd 到那个目录,执行 python clean_excel.py。等着屏幕上跳出「搞定」就行。
同事五点半下班的时候看了一眼我的屏幕:「你那个脚本,能发我一份吗?」
我说:「发你了。」
他下一句话:「下一篇你是不是又要写出来了?」
我说:「已经写了。」
下次遇到什么 Excel 折磨你的场景?评论区说说,我挑个最有共鸣的写。
关注我,每周分享一个让你准时下班的技术小技巧。
夜雨聆风