资讯动态

Pandas数据清洗与预处理完整流程:类型转换、缺失值处理与正则实战

发布时间:2026/9/29 2:14:01 来源:尧图企业网站定制
干数据分析这行久了只会越来越明白一个道理真正的分析工作很大一部分不是模型选型不是调参而是面对一张张“脏乱差”的原始表。Pandas作为Python生态里最主流的数据处理库在数据清洗和预处理这个环节几乎是绕不开的工具。无论你处理的是网约车订单日志、农产品价格日报、招聘网站抓下来的岗位信息还是校园数据、实验作业里的报表第一件事永远是先用Pandas做一轮摸底数据长什么样、有多少缺失、类型对不对、重复多不多。这篇文章把我这些年用Pandas做数据清洗和预处理的经验整理成一套能直接复用的流程从环境搭建到核心操作、从完整案例到高频坑位的排查新手能照着走老手也能拿来排查思路。1. 整体思路拆解为什么数据清洗非用Pandas不可1.1 脏数据到底“脏”在哪先搞清楚我们要处理什么拿到的原始数据就像刚出土的土豆上面全是泥你不能直接下锅。泥的种类其实很固定。我把它们分成四类结构型脏列名不统一、同一列里混着多种类型、日期格式千奇百怪、索引重置不干净。记录型脏行缺失、列缺失、完全重复的行、主键重复的记录。内容型脏字符串前后有空格、手机号少一位、薪资写成“10K-15K”、文本里混着制表符和乱码。逻辑型脏年龄填成负数、价格出现0元、下单时间晚于发货时间这类问题不靠语法查得靠业务常识判断。在动手清洗之前我建议永远先做一件事用df.info()和df.describe()快速看整体结构再用df.head()抽几行肉眼看内容。很多人一上来就fillna、dropna结果把有用的信息也误删了。先摸清“泥”的类型再决定用什么工具这是Pandas数据清洗的第一步也是最长经验的一步。1.2 Pandas拆解数据清洗问题的思路表驱动与向量化Pandas之所以成为数据清洗首选核心在于它把数据抽象成DataFrame这张“表”。你可以把它想成加强版Excel但它和Excel有个根本区别DataFrame里的每一列是有明确数据类型的所有操作都按列批量进行。举个例子你要把“入职日期”从字符串“2023/7/15”变成标准时间格式。在Excel里你得写公式、拖拽、再复制成值在Pandas里一行代码搞定df[入职日期] pd.to_datetime(df[入职日期])这种“向量化”思维贯穿所有清洗步骤。整列处理、整列比较、整列替换不需要写循环。清洗代码写成链式调用还特别好读df_clean ( df.drop_duplicates(subset[订单号]) .dropna(subset[金额]) .assign(订单日期lambda x: pd.to_datetime(x[订单日期])) )对比SQLPandas的优势在于和Python生态无缝衔接清洗完可以直接接上matplotlib做可视化或接sklearn做建模。对比纯手写循环Pandas更不会因为数据量大而卡死。所以我的判断标准很简单只要数据能丢进内存就优先用Pandas如果数据大到单机扛不住再考虑Spark或分块策略但清洗逻辑和Pandas这套思路基本一致只是换了个分布式壳子。2. 核心操作拆解类型、缺失值、文本清洗一个都不能少2.1 数据类型转换先把“表”修好后面分析才不翻车Pandas里最容易被忽视的一步就是数据类型。真实项目里数字列经常是object类型日期列是字符串Boolean列甚至混着“是/否/1/0”。这直接导致后续排序、聚合、画图全部出错。我总结了一套固定打法。第一用df.dtypes全列摸底。尤其在pandas 2.x里默认dtype的显示更清楚了一眼能看出object包裹的数值列。Series和DataFrame这两个基础结构平时不用刻意记但你要知道很多底层清洗函数作用在Series上比如pd.to_numeric处理的就是单列。第二数值列统一走pd.to_numeric带上errorscoerce。比如订单金额列里混了“--”、空字符串直接astype会崩溃用to_numeric会把非法值变成NaN先保住大部分数据df[金额] pd.to_numeric(df[金额], errorscoerce)拿到NaN之后再决定是填充还是丢弃这个放到缺失值处理里一起说。第三日期列统一走pd.to_datetime。它的解析能力很强像“2023/7/15”“20230715”“15-Jul-2023”这种格式都能识别。只有遇到混合格式特别离谱时才需要指定format比如pd.to_datetime(df[日期], format%Y%m%d)format指定后解析速度会快很多适用于上百万行的大表。第四布尔列直接astype(bool)。但注意NaN转bool会直接变True这是个巨坑。所以先fillna再转或者用map把“是/否”映射成True/Falsedf[是否有效] df[是否有效].map({是: True, 否: False})这里说一个我自己踩过很多次的坑修改列类型时忘了写df[列] 。astype不是inplace操作它返回新列不赋值就静默丢弃排查半天才发现类型没变。类似的还有inplace参数建议统一使用“赋值式”写法代码可读性也好。2.2 缺失值与重复值先看比例再动手别一上来就删处理缺失值的第一个原则不要盲删。要先用df.isnull().sum()看一下每列缺失的数量和比例。我见过有人把缺失占比60%的列直接dropna删掉结果后续分析根本没法做因为那一列是核心指标。根据缺失比例我的清洗策略一般是这样的缺失情况处理策略少于5%直接删除对应的行影响很小5%-30%用均值/中位数/众数填充或用前后值填充超过30%考虑删除整列或做“缺失值”标记追加一列is_missing填充时不一定都用均值。对于时间序列数据比如农产品价格日报用ffill用上一个值填充更符合真实波动对于订单类数据按分组填充比如按城市分组填该城市的中位数比全局填充更合理df[价格] df.groupby(城市)[价格].transform(lambda x: x.fillna(x.median()))重复值处理比较简单但也别做“一刀切”的drop_duplicates。关键要看业务主键比如一个订单表里订单号相同但状态不同退款、已完成只保留第一行会丢信息。建议先df.duplicated().sum()看重复量再用subset指定判断重复的列用keep参数决定保留哪行df.drop_duplicates(subset[订单号, 商品编号], keeplast)很多在线实训平台的pandas基本操作、数据结构创建作业里都会考duplicated和drop_duplicates但实际项目里重复值往往不是简单删行而是“多个状态取最新”或“合并多条记录”所以先理解业务再写代码。2.3 文本清洗与正则表达式对付“脏字符串”的主力战法如果说类型和缺失值是“明枪”那文本里的乱七八糟格式就是“暗箭”。地址栏里多个空格、手机号中间混着横杠、邮箱后缀大小写不一这些内容型脏数据最烦人。Pandas的str访问器是我最常用的工具。它让整列字符串操作变得和Python字符串一样方便df[地址] df[地址].str.strip() # 去首尾空格 df[电话] df[电话].str.replace(-, ) # 去横杠 df[城市] df[地址].str.extract(r([\u4e00-\u9fa5]?市)) # 提取城市正则表达式是文本清洗的进阶核心。热搜词里有个“pandas 正在表达式”其实就是“正则表达式”的误写但确实很多人卡在这一块。我在清洗招聘数据时经常用正则把“10K-15K”拆成最低薪资和最高薪资salary df[薪资].str.extract(r(\d)K-(\d)K) df[最低薪资] salary[0].astype(float) df[最高薪资] salary[1].astype(float)手机号提取df[手机] df[联系方式].str.extract(r(1[3-9]\d{9}))邮箱校验df[有效邮箱] df[邮箱].str.match(r^[\w\.-][\w\.-]\.\w$)用正则做文本清洗有一条经验先提取再删除。先把有效信息提取成新列再对旧列做替换或填充比试图用正则“一步到位”更稳。正则写错了最多返回空不会报错所以一定要加测试样本验证。3. 从零开始的完整实操环境、读取与一份招聘数据清洗案例3.1 环境准备Pycharm里安装Pandas的几个常见坑Pycharm怎么安装pandas包这是热搜词里出现很多的问题也是每个新手的第一道坎。我推荐三种方法。方法一直接在Pycharm右下角或设置里的Python Packages窗口搜索pandas点Install。这个方法最直观适合刚上手的人。方法二在Pycharm底部打开Terminal输一行命令pip install pandas如果下载太慢大概率是默认源在国外。临时换源pip install pandas -i https://pypi.tuna.tsinghua.edu.cn/simple方法三如果你用Anaconda环境在Terminal里用conda安装会更省心conda install pandas常见问题是装完import pandas报错“ModuleNotFoundError”。这时候先检查解释器Pycharm右下角显示的Python环境有时候切换了虚拟环境但没装包。另一个问题是pip和python版本不对应用python -m pip install pandas最稳妥。3.2 文件读取CSV、Excel与文本文件的参数差异数据清洗的第一步通常是把文件读进DataFrame。read_csv用得最多但很多参数一开始容易被忽略导致“读进去就是脏的”。先说说编码。国内业务数据最常见的是GBK/GB2312read_csv默认用utf-8所以一读就报UnicodeDecodeError。遇到这种情况直接指定encodingdf pd.read_csv(data.csv, encodinggbk)或者用encodingutf-8加errorsignore跳过坏字符但我不推荐后者容易静默丢字符。再聊聊sep参数。CSV不一定都是逗号分隔有些导出文件用制表符\t、分号甚至竖线。用sep\t、sep;、sep|都能解决。还有一个坑是read_csv自动推断分隔符对大文件可能推断错所以最好显式指定。读取文本文件也一样分隔符是制表符就直接df pd.read_csv(data.txt, sep\t, headerNone)没有表头的txt文件配headerNone再手动给它绑定列名。Excel文件用read_excel注意它依赖openpyxl或xlrd库。其中的sheet_name参数可以指定读取哪个工作表也可以传一个列表读多个表。dtype参数在读取时就可以指定列的类型比如“订单号”这种以0开头的字段如果不指定dtype会被读成int丢掉前导零df pd.read_excel(订单.xlsx, sheet_name明细, dtype{订单号: str})我的经验是读文件时把列名、编码、类型一次配好能省掉后面大量的补救步骤。3.3 完整演示一份招聘数据从“脏乱差”到可分析这个案例我选的是招聘数据清洗这也是热搜词里多次出现的场景。假设我们拿到了这样一份抓取数据包含岗位名称、公司、薪资、工作地点、发布时间、经验要求六个字段。第一步读取与初步探查import pandas as pd df pd.read_csv(zhaopin.csv, encodinggbk) print(df.info()) print(df.head())第二步观察后的典型问题薪资列是“10K-15K”这种字符串工作地点是“北京-海淀区”这种组合经验要求是“3-5年”发布时间的格式有“2023-07-15”“2023/7/15”两种公司列有缺失岗位名称有重复。第三步依次清洗# 1. 去掉完全重复的行 df df.drop_duplicates() # 2. 公司名缺失的行如果不是核心维度直接删除 df df.dropna(subset[公司]) # 3. 薪资解析拆成最低和最高薪资 salary df[薪资].str.extract(r(\d)K-(\d)K) df[最低薪资] salary[0].astype(float) df[最高薪资] salary[1].astype(float) df[平均薪资] (df[最低薪资] df[最高薪资]) / 2 # 4. 工作地点拆分成城市和区域 loc df[工作地点].str.split(-, expandTrue) df[城市] loc[0] df[区域] loc[1] # 5. 经验要求提取数字 df[最低年限] df[经验要求].str.extract(r(\d)).astype(float) # 6. 发布时间统一格式 df[发布时间] pd.to_datetime(df[发布时间])第四步最终检查print(df.isnull().sum()) print(df.dtypes) print(df.head())清洗完之后这张表可以直接用于薪资分析、城市分布统计、经验年限与薪资的关系探索。实际做的时候我还会加一步把清洗逻辑封装成函数因为招聘数据会周期性更新每月跑一次同样的流程事半功倍。这种把清洗过程“工程化”的思路在网约车、校园数据、农产品价格等任何有周期更新需求的项目里都通用。4. 高频问题与排查技巧实录4.1 编码与分隔符问题先别急着改数据改读取参数我统计过自己处理过的数据文件90%的读取报错都出在两个地方编码和分隔符。UnicodeDecodeError的解决方案上面已经给了更隐蔽的是文件能用utf-8读进来但某些单元格里混着特殊字符sort或groupby时突然报错。这种时候可以先df[列].apply(lambda x: repr(x))看一下原始字符。分隔符问题则是读进来的列数量不对或者第一列挤成一团。处理方案是显式指定sep或者先用文本编辑器打开文件确认真正的分隔符是什么。有些文件是多个空格分隔但不是固定宽度read_fwf会比read_csv更合适。4.2 SettingWithCopyWarning与类型转换的隐藏陷阱SettingWithCopyWarning是Pandas新手最容易遇到、也最容易忽视的告警。本质上是因为你对一个切片后的DataFrame赋值操作没有生效或只生效在副本上。我推荐的解决方案很简单所有清洗都基于“取出来重新赋值”的模式不在一行里链式修改。比如# 危险写法 df[df[城市] 北京][平均薪资] 20 # 安全写法 df.loc[df[城市] 北京, 平均薪资] 20另外一个隐藏陷阱是astype失败。object列里看起来都是数字但astype(int)报错多半是里面有空格或者字符串“None”。先用pd.to_numeric(errorscoerce)转一遍把脏值变成NaN后再处理要比直接astype安全得多。4.3 大文件与性能瓶颈用向量化操作替代一切循环数据量一上来Pandas的性能问题就暴露了。最常见的错误是用iterrows循环逐行处理处理几万行还行到几十万行就开始卡。我的经验是能用向量化操作解决的绝不写循环实在要复杂逻辑优先用apply循环是最后选项。读取大文件时用chunksize分块chunk_list [] for chunk in pd.read_csv(big.csv, chunksize100000): chunk_clean clean_func(chunk) chunk_list.append(chunk_clean) df pd.concat(chunk_list)还有一个容易被忽略的优化点是数据类型。把分类字段转成category类型数值列能转小类型就转小类型内存占用能降一半。用df.memory_usage(deepTrue)可以查各列内存占用先优化占大头的列。5. 从通用清洗走向场景化预处理数据清洗不是终点5.1 价格、位置、时间三类高频场景的清洗共性把热搜词里那些场景摆在一起看农产品价格数据清洗、网约车大数据综合项目清洗、招聘数据清洗、夜间灯光数据预处理、地图数据预处理……听起来行业跨度很大但真正落到Pandas代码上重复出现的永远是三类问题价格/数值类、位置/空间类、时间类。价格类的核心是去掉单位、拆范围、过滤异常。比如农产品价格里出现0元、土鸡蛋800元/斤这类明显异常值要靠业务经验设阈值过滤。位置类的核心是拆分省市区、补全邮编、坐标、统一命名。比如网约车订单里的“北京市朝阳区”和“朝阳区”指同一个地方但匹配起来会失败需要清洗成统一粒度。像夜间灯光遥感这类空间数据清洗重点就变成了无效值剔除、像元提取和坐标信息核对套路和普通表格数据不太一样但第一步仍然是用Pandas把文件名、波段、日期这些元数据整理干净。时间类的核心是格式化、补缺失、拆解维度。比如“2023/7/15”和“20230715”混在一起pd.to_datetime能解一部分但遇到“07/15/2023”这种美式格式就得指定format。理解了这三个共性换个行业只要套模板就行。这也是为什么Pandas数据清洗的代码能这么通用。如果数据量到了亿级会换成Spark或者MapReduce这类分布式框架但清洗思路依然是Pandas这套逻辑的扩展。5.2 清洗之后从Pandas清洗走向完整预处理闭环数据清洗只是第一步预处理还包括后续的数据变换和特征工程。Pandas能把清洗结果直接接到sklearn的pipeline里这是它作为预处理工具的最大优势。在实际项目里我的习惯是清洗阶段输出一份“干净版”数据单独保存成parquet或csv然后在这个基础上做特征工程比如标准化、One-Hot编码、分箱。Pandas的get_dummies做分类变量编码很方便df_encoded pd.get_dummies(df, columns[城市])分箱可以用pd.cut和pd.qcutdf[价格等级] pd.qcut(df[平均薪资], q4, labels[低, 中, 高, 很高])之所以强调“清洗和预处理分开”是为了让每一步都可追溯。如果后面发现特征有问题回退到清洗数据重新做而不是在一锅里捞针。这个工程习惯比任何一行代码都值钱。做Pandas数据清洗这么多年我自己最大的体会是资料上教的都是函数真实项目里考的是判断。你什么时候该删行、什么时候该填值、什么时候该把一列拆成两列这些判断只能在一次次的实操里积累。建议你也从一份真实的“脏数据”开始照着文章里的流程走一遍遇到问题别急着搜答案先看数据本身再看函数文档。等你把缺失值、类型转换、文本正则、性能优化这些环节都亲手做一遍Pandas数据清洗和预处理这关才算真正过了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑