Python数据清洗全攻略与实战技巧
本文深入解析了Python数据清洗的全流程,旨在帮助读者系统性地处理“脏数据”,将其转化为高质量的结构化数据,为后续分析和模型训练奠定坚实基础。数据清洗涉及多个关键环节,包括缺失值处理(删除或填充)、重复项消除、格式统一(大小写、空格、数据类型转换)、异常值识别与处理,以及数据一致性检查。文章强调,数据清洗是一个迭代过程,需要深入理解数据本身,并结合Pandas等库的强大功能,例如`dropna()`、`fillna()`、`drop_duplicates()`等,以及`info()`、`describe()`、`isnull().sum()`等方法进行初步探索。通过实战示例,详细阐述了各种清洗策略的应用,如均值、中位数填充缺失值,以及利用IQR识别异常值,助力读者掌握数据清洗的核心技能。
数据清洗的核心在于系统性处理缺失值、重复项、格式不一致、异常值及逻辑错误,以提升数据质量。1. 缺失值可通过删除或填充处理,依据缺失比例与数据特性选择合适策略;2. 重复项需明确重复定义,使用drop_duplicates()清除;3. 格式不一致应统一大小写、去除空格,并转换为正确数据类型;4. 异常值通过统计方法(如IQR)识别,结合业务判断删除、替换或转换;5. 数据一致性检查需验证字段间逻辑关系,确保数据合理性。整个过程依赖对数据的深入理解,且通常需反复迭代。
用Python清洗杂乱数据,核心在于利用Pandas等库,系统性地处理缺失值、重复项、格式不一致及异常值,将原始的“脏数据”转化为可分析、高质量的结构化数据。这通常是数据项目中最耗时但也最关键的第一步,它直接决定了后续分析和模型训练的可靠性。

解决方案
数据清洗是一个迭代的过程,没有一劳二的万能公式,更像是一场侦探游戏,需要你深入了解数据本身。我的做法通常是:先加载数据并进行初步探索,用info()
、describe()
、head()
、isnull().sum()
这些方法快速摸清家底。然后,针对性地处理缺失值,决定是填充、删除还是插值。紧接着,我会检查并移除重复项,统一数据格式,比如日期、文本大小写,确保数据类型正确无误。最后,处理那些“不合群”的异常值,并进行一些逻辑校验,确保数据在业务层面上也是合理的。这个过程往往不是线性的,可能需要反复检查和调整。
数据清洗,到底洗什么?——识别常见脏数据类型
说起数据清洗,很多人第一反应就是“把数据弄干净”,但“干净”到底意味着什么?我个人的理解是,让数据变得“可用”且“可靠”。这其中要处理的“脏”东西,大致有这么几类:

- 缺失值 (Missing Values):这是最常见的。数据采集中断、用户未填写、系统错误……各种原因都可能导致数据字段为空。它们可能是
NaN
、None
,甚至是空字符串或特定的占位符(比如NA
、-
)。如果直接拿去分析,轻则报错,重则结果偏差。 - 重复值 (Duplicate Values):有时候,同样的数据记录会被不小心录入多次。比如,一个用户注册了两次,或者同一笔交易被记录了两遍。这些重复项会虚增你的数据集大小,扭曲统计结果。
- 格式不一致 (Inconsistent Formats):这尤其体现在文本和日期字段上。比如,表示性别的有“男”、“M”、“male”,日期的有“2023-01-01”、“01/01/2023”、“Jan 1, 2023”。这些看似相同的信息,在计算机眼里却是完全不同的字符串,直接影响聚合和比较。
- 数据类型错误 (Incorrect Data Types):数字被当作字符串存储,日期被当作普通对象。这会导致你无法进行数值计算或日期时间操作,甚至排序都可能出错。
- 异常值/离群点 (Outliers):某些数据点的值远远偏离了大多数数据。它们可能是录入错误(比如年龄写成了200岁),也可能是真实但极端的情况(比如某个用户消费额是天文数字)。处理不好,它们会严重拉高或拉低平均值,影响模型表现。
- 逻辑错误 (Logical Errors):这是比较隐蔽的一类。比如,一个订单的完成日期早于下单日期,或者一个人的年龄是负数。这些数据在格式上可能没错,但从业务逻辑上讲是错的。
识别这些问题,往往需要结合df.info()
、df.describe()
、df.isnull().sum()
、df.duplicated().sum()
,以及一些可视化工具(如箱线图、直方图)来辅助判断。
import pandas as pd import numpy as np # 示例数据 data = { 'ID': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 'Name': ['Alice', 'Bob', 'Charlie', 'Alice', 'David', 'Eve', 'Frank', 'Grace', 'Heidi', 'Ivan'], 'Age': [25, 30, np.nan, 25, 40, 22, 35, 28, 999, 30], 'City': ['New York', 'London', 'paris', 'New York', 'Berlin', 'London', 'Paris ', 'New York', 'London', 'Berlin'], 'Income': [50000, 60000, 75000, 50000, np.nan, 45000, 80000, 55000, 70000, 65000], 'JoinDate': ['2023-01-15', '2023/02/20', '2023-03-10', '2023-01-15', '2023-04-05', '2023-05-01', '2023-06-12', '2023-07-01', '2023-08-08', '2023-09-01'] } df = pd.DataFrame(data) print("--- 初始数据信息 ---") df.info() print("\n--- 缺失值统计 ---") print(df.isnull().sum()) print("\n--- 重复行统计 ---") print(f"总重复行数: {df.duplicated().sum()}") print("\n--- 描述性统计 (发现异常值) ---") print(df.describe())
缺失值的处理:补齐还是删除?Python实战策略
处理缺失值,这真的是数据清洗里最让人头疼也最考验经验的一环。究竟是删除含有缺失值的行或列,还是想办法填充它们?这没有绝对的答案,完全取决于你的数据量、缺失值的比例、缺失模式以及你后续的分析目的。

删除 (Dropping):
如果某个特征的缺失值比例非常高(比如超过70-80%),或者某个观察值(行)大部分数据都缺失,那么删除它们可能是最直接有效的办法。df.dropna()
是你的利器。
df.dropna()
:删除任何含有NaN的行。df.dropna(how='all')
:只删除所有值都为NaN的行。df.dropna(axis=1)
:删除任何含有NaN的列。df.dropna(thresh=N)
:删除至少有N个非NaN值的行。df.dropna(subset=['列名1', '列名2'])
:只考虑特定列的缺失值来删除行。
我个人在使用dropna()
时非常谨慎,尤其是删除行。如果数据量不大,或者缺失值不是随机分布的,盲目删除可能导致信息丢失,甚至引入新的偏差。
填充 (Imputation):
当缺失值比例不高,且你认为这些缺失值背后可能蕴含着某种信息时,填充是更好的选择。df.fillna()
是Pandas提供的强大功能。
- 固定值填充:
df['列名'].fillna(0)
或df['列名'].fillna('未知')
。适用于你知道缺失值应该是什么特定值的情况。 - 统计量填充:
- 均值 (Mean):
df['Age'].fillna(df['Age'].mean())
。适用于数值型数据,且数据分布接近正态分布时。 - 中位数 (Median):
df['Age'].fillna(df['Age'].median())
。比均值更健壮,不易受异常值影响,适用于偏态分布的数值数据。 - 众数 (Mode):
df['City'].fillna(df['City'].mode()[0])
。适用于类别型数据,或数值型数据中众数有明确意义的情况。注意mode()
可能返回多个众数,通常取第一个。
- 均值 (Mean):
- 前向填充 (Forward Fill,
ffill
):df['列名'].fillna(method='ffill')
。用前一个有效值填充当前缺失值。常用于时间序列数据,假设缺失值与前一个值相同。 - 后向填充 (Backward Fill,
bfill
):df['列名'].fillna(method='bfill')
。用后一个有效值填充当前缺失值。 - 插值 (Interpolation):
df['列名'].interpolate()
。根据已知值的趋势来推断缺失值,适用于数值型数据,特别是时间序列或有序数据。支持多种插值方法,如线性、多项式等。
我的经验是,对于数值型缺失值,如果分布偏态,中位数往往是比均值更稳妥的选择。对于类别型数据,众数填充通常比较合理。对于时间序列,ffill
或bfill
,甚至更复杂的序列模型插值会更合适。没有哪种方法是完美的,选择最合适的,需要你对数据有足够的了解和判断。
# 处理缺失值示例 # 复制一份数据,避免影响后续操作 df_cleaned_missing = df.copy() # 1. 删除缺失值:例如,如果Name缺失,则认为这条记录无效 # df_cleaned_missing.dropna(subset=['Name'], inplace=True) # 这里Name没有缺失,所以不执行 # 2. 填充缺失值 # 填充Age的缺失值:使用中位数,因为Age可能有异常值(999) age_median = df_cleaned_missing['Age'].median() df_cleaned_missing['Age'].fillna(age_median, inplace=True) print(f"\n--- Age列缺失值填充后 (中位数: {age_median}) ---") print(df_cleaned_missing['Age']) # 填充Income的缺失值:使用均值,假设Income分布相对均匀 income_mean = df_cleaned_missing['Income'].mean() df_cleaned_missing['Income'].fillna(income_mean, inplace=True) print(f"\n--- Income列缺失值填充后 (均值: {income_mean:.2f}) ---") print(df_cleaned_missing['Income']) print("\n--- 缺失值处理后的统计 ---") print(df_cleaned_missing.isnull().sum())
统一数据格式与消除重复:让数据整洁有序
数据清洗的这部分工作,就像是整理你的衣柜,把散乱的衣服叠好,把重复的款式挑出来。它看起来琐碎,但却是构建可靠数据基础的基石。
统一数据格式: 数据类型不正确,就像是把鞋子放进了帽子抽屉,用起来非常别扭。
- 数据类型转换 (
astype()
,to_datetime()
,to_numeric()
):- 确保数值列是数值类型,否则无法进行计算。
- 确保日期列是日期时间类型,这样才能进行日期相关的操作(如按月聚合、计算时间差)。
- 文本列有时候也需要转换成
category
类型,特别是当类别数量有限时,可以节省内存并加速某些操作。
- 文本标准化 (
str.lower()
,str.strip()
,str.replace()
):- 比如,
City
列有“New York”、“new york”、“Paris ”、“paris”。这些都是同一个城市,但大小写和空格导致它们被视为不同。 df['City'].str.lower()
:统一转为小写。df['City'].str.strip()
:去除字符串两端的空格。df['City'].str.replace('paris', 'Paris', case=False)
:更复杂的替换,比如把所有“paris”都统一成“Paris”。- 这部分工作需要你对文本内容有一定了解,甚至需要用到正则表达式来处理更复杂的模式。
- 比如,
消除重复项 (drop_duplicates()
):
重复数据是统计分析中的一大陷阱,它会虚高你的计数,扭曲平均值,让模型训练时学到错误的信息。
- 识别重复行:
df.duplicated()
会返回一个布尔Series,指示每行是否是重复的(默认为除了第一次出现外的所有重复行)。 - 删除重复行:
df.drop_duplicates()
是最常用的方法。df.drop_duplicates(inplace=True)
:直接在原DataFrame上修改。df.drop_duplicates(subset=['列名1', '列名2'])
:只考虑特定列的组合来判断是否重复。比如,你可能认为只要ID
和Name
都一样,就是重复记录。df.drop_duplicates(keep='first')
:保留第一次出现的重复项(默认)。df.drop_duplicates(keep='last')
:保留最后一次出现的重复项。df.drop_duplicates(keep=False)
:删除所有重复项(包括第一次和最后一次出现的)。
我的建议是,在删除重复项时,一定要明确你“重复”的定义是什么。是所有列都一样才算重复,还是部分关键列一样就算重复?这直接影响你的数据完整性和后续分析。
# 复制一份数据,基于前面处理缺失值后的数据 df_final_cleaned = df_cleaned_missing.copy() # 1. 统一数据格式 # 将JoinDate转换为datetime类型 df_final_cleaned['JoinDate'] = pd.to_datetime(df_final_cleaned['JoinDate'], errors='coerce') # errors='coerce' 会将无法解析的日期转换为NaT (Not a Time) # 统一City列的格式:小写并去除首尾空格 df_final_cleaned['City'] = df_final_cleaned['City'].str.lower().str.strip() # 进一步标准化城市名称,例如 'paris' -> 'Paris' df_final_cleaned['City'] = df_final_cleaned['City'].replace({'paris': 'Paris', 'new york': 'New York', 'london': 'London', 'berlin': 'Berlin'}) # 2. 消除重复项 # 检查基于ID和Name的重复 print(f"\n--- 基于ID和Name的重复行数量: {df_final_cleaned.duplicated(subset=['ID', 'Name']).sum()} ---") # 发现ID=1, Name='Alice' 和 ID=4, Name='Alice' 是重复的,但ID不同,所以不是完全重复的行。 # 如果我们认为ID是唯一标识,那么ID=4的Alice是重复的(假设ID是唯一用户标识) # 这里我们发现ID 1和4的Name都是Alice,但ID不同。 # 如果我们认为ID是唯一的,那么就没有完全重复的行。 # 如果我们认为'Name'列的'Alice'重复了,那需要更复杂的去重逻辑。 # 对于本例,我们假设整行完全重复才算。 print(f"\n--- 完全重复行数量: {df_final_cleaned.duplicated().sum()} ---") # 发现第1行和第4行是完全重复的 (ID=1, Name='Alice', Age=25, City='New York', Income=50000.0, JoinDate='2023-01-15') # 实际上,ID是主键,ID=1和ID=4的Alice是两个不同的人。 # 如果ID是唯一标识,那么ID=4的Alice是重复录入的ID=1的Alice,应该删除ID=4。 # 让我们假设ID是唯一标识,删除ID=4的重复记录。 # 在本例中,df中ID=1和ID=4的记录除了ID不同,其他完全一样。 # 如果我们想删除基于ID的重复,即ID唯一,那么应该删除ID=4。 # 更好的做法是,如果我们认为ID是唯一键,那么就直接删除ID列的重复。 df_final_cleaned.drop_duplicates(subset=['ID'], keep='first', inplace=True) # 确保ID唯一 print("\n--- 清洗后的数据预览 ---") print(df_final_cleaned.head()) print("\n--- 清洗后数据信息 ---") df_final_cleaned.info() print("\n--- 清洗后City列唯一值 ---") print(df_final_cleaned['City'].unique())
异常值与数据一致性:精炼你的数据集
数据清洗的最后阶段,往往是处理那些“不合群”的异常值,以及确保数据在逻辑上是自洽的。这部分工作非常依赖你对业务的理解,因为一个“异常”的数据点,可能是错误,也可能是真实的极端情况。
异常值处理 (Outlier Treatment):
- 识别方法:
- 统计方法:
- Z-score:如果数据近似正态分布,Z-score(数据点与均值的距离,以标准差为单位)超过某个阈值(如2或3)可能就是异常值。
- IQR (Interquartile Range):对于非正态分布数据更稳健。Q1 - 1.5 IQR 下限,Q3 + 1.5 IQR 上限,超出这个范围的认为是异常值。
- 可视化方法:
- 箱线图 (Box Plot):直观显示数据的分布、中位数、四分位数和异常值。
- 散点图 (Scatter Plot):对于二维数据,可以直观看出离群点。
- 统计方法:
- 处理策略:
- 删除:如果异常值数量很少,且确认是录入错误,可以直接删除。但要非常小心,避免删除真实数据。
- 替换/修正:将异常值替换为均值、中位数,或者替换为边界值(如IQR的上下限,这种方法叫Winsorization)。
- 转换:对数据进行对数、平方根等转换,可以减小异常值的影响,使数据分布更接近正态。
- 不处理:如果异常值是真实的极端情况,且对你的分析有重要意义,那么可能不需要处理,但需要在分析中特别指出。
我个人在处理异常值时,总是先问自己:这个异常值是“错”的,还是“特别”的?错误的就修正或删除,特别的则需要特殊对待,而不是一刀切。比如,我们示例数据中
Age
列的999。
数据一致性检查 (Data Consistency Checks): 这超出了简单的格式统一,更多是关于数据之间的逻辑关系。
- 范围校验:例如,年龄必须在0到120之间,销售额不能为负数。
- 交叉字段校验:例如,如果一个订单状态是“已发货”,那么发货日期就不能是空的。或者,一个人的出生日期不能晚于死亡日期。
- 唯一性校验:确保主键(如用户ID、订单号)是唯一的。
这部分通常需要编写自定义的函数或规则来执行。
# 继续在df_final_cleaned上操作 # 1. 异常值处理 # 处理Age列的异常值 (999) # 使用IQR方法来识别和处理 Q1 = df_final_cleaned['Age'].quantile(0.25) Q3 = df_final_cleaned['Age'].quantile(0.75) IQR = Q3 - Q1 lower_bound = Q1 - 1.5 * IQR upper_bound = Q3 + 1.5 * IQR # 识别异常值 outliers = df_final_cleaned[(df_final_cleaned['Age'] < lower_bound) | (df_final_cleaned['Age'] > upper_bound)] print(f"\n--- Age列IQR方法识别的异常值: ---\n{outliers}") # 对于Age的999,明显是录入错误,可以考虑替换为中位数或直接删除
以上就是《Python数据清洗全攻略与实战技巧》的详细内容,更多关于Python,异常值,数据清洗,Pandas,缺失值的资料请关注golang学习网公众号!

- 上一篇
- 用CSS显示作者元数据技巧

- 下一篇
- 全屏视频背景优化4种方法详解
-
- 文章 · python教程 | 4分钟前 |
- Python语音合成教程:pyttsx3实战详解
- 493浏览 收藏
-
- 文章 · python教程 | 17分钟前 |
- Dask并行处理,Python高效处理千万数据教程
- 222浏览 收藏
-
- 文章 · python教程 | 21分钟前 |
- Python开发区块链的简单教程
- 407浏览 收藏
-
- 文章 · python教程 | 23分钟前 |
- Django入门:PythonWeb开发教程
- 328浏览 收藏
-
- 文章 · python教程 | 27分钟前 |
- PyCharm安装后怎么打开?首次启动步骤详解
- 460浏览 收藏
-
- 文章 · python教程 | 38分钟前 |
- PythonPlotly交互图表制作教程
- 102浏览 收藏
-
- 文章 · python教程 | 45分钟前 |
- Python音频频谱分析:librosa实战教程
- 275浏览 收藏
-
- 文章 · python教程 | 1小时前 |
- 正向预查与负向预查有什么不同
- 461浏览 收藏
-
- 文章 · python教程 | 1小时前 |
- Python递归解析自定义配置文件技巧
- 137浏览 收藏
-
- 文章 · python教程 | 1小时前 | 性能优化 文本处理 模式匹配 Python正则表达式 re模块
- Python正则表达式实用技巧分享
- 349浏览 收藏
-
- 文章 · python教程 | 1小时前 |
- Python中elif是什么?条件判断详解
- 323浏览 收藏
-
- 文章 · python教程 | 1小时前 |
- Python处理PDF技巧:PyPDF2功能详解
- 209浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 511次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 498次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 484次学习
-
- 扣子-Space(扣子空间)
- 深入了解字节跳动推出的通用型AI Agent平台——扣子空间(Coze Space)。探索其双模式协作、强大的任务自动化、丰富的插件集成及豆包1.5模型技术支撑,覆盖办公、学习、生活等多元应用场景,提升您的AI协作效率。
- 11次使用
-
- 蛙蛙写作
- 蛙蛙写作是一款国内领先的AI写作助手,专为内容创作者设计,提供续写、润色、扩写、改写等服务,覆盖小说创作、学术教育、自媒体营销、办公文档等多种场景。
- 12次使用
-
- CodeWhisperer
- Amazon CodeWhisperer,一款AI代码生成工具,助您高效编写代码。支持多种语言和IDE,提供智能代码建议、安全扫描,加速开发流程。
- 30次使用
-
- 畅图AI
- 探索畅图AI:领先的AI原生图表工具,告别绘图门槛。AI智能生成思维导图、流程图等多种图表,支持多模态解析、智能转换与高效团队协作。免费试用,提升效率!
- 54次使用
-
- TextIn智能文字识别平台
- TextIn智能文字识别平台,提供OCR、文档解析及NLP技术,实现文档采集、分类、信息抽取及智能审核全流程自动化。降低90%人工审核成本,提升企业效率。
- 65次使用
-
- Flask框架安装技巧:让你的开发更高效
- 2024-01-03 501浏览
-
- Django框架中的并发处理技巧
- 2024-01-22 501浏览
-
- 提升Python包下载速度的方法——正确配置pip的国内源
- 2024-01-17 501浏览
-
- Python与C++:哪个编程语言更适合初学者?
- 2024-03-25 501浏览
-
- 品牌建设技巧
- 2024-04-06 501浏览