乐于分享
好东西不私藏

几百张Excel表格全是脏数据?我把同事折磨了一下午的活,写成了这篇

几百张Excel表格全是脏数据?我把同事折磨了一下午的活,写成了这篇

上周同事接了个活——公司要做年度数据分析,需要把各个部门报上来的 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 折磨你的场景?评论区说说,我挑个最有共鸣的写。


关注我,每周分享一个让你准时下班的技术小技巧。

相关学习资料