当前位置:首页 > 文章列表 > 文章 > python教程 > Python数据清洗全攻略与实战技巧

Python数据清洗全攻略与实战技巧

2025-07-20 16:36:43 0浏览 收藏

本文深入解析了Python数据清洗的全流程,旨在帮助读者系统性地处理“脏数据”,将其转化为高质量的结构化数据,为后续分析和模型训练奠定坚实基础。数据清洗涉及多个关键环节,包括缺失值处理(删除或填充)、重复项消除、格式统一(大小写、空格、数据类型转换)、异常值识别与处理,以及数据一致性检查。文章强调,数据清洗是一个迭代过程,需要深入理解数据本身,并结合Pandas等库的强大功能,例如`dropna()`、`fillna()`、`drop_duplicates()`等,以及`info()`、`describe()`、`isnull().sum()`等方法进行初步探索。通过实战示例,详细阐述了各种清洗策略的应用,如均值、中位数填充缺失值,以及利用IQR识别异常值,助力读者掌握数据清洗的核心技能。

数据清洗的核心在于系统性处理缺失值、重复项、格式不一致、异常值及逻辑错误,以提升数据质量。1. 缺失值可通过删除或填充处理,依据缺失比例与数据特性选择合适策略;2. 重复项需明确重复定义,使用drop_duplicates()清除;3. 格式不一致应统一大小写、去除空格,并转换为正确数据类型;4. 异常值通过统计方法(如IQR)识别,结合业务判断删除、替换或转换;5. 数据一致性检查需验证字段间逻辑关系,确保数据合理性。整个过程依赖对数据的深入理解,且通常需反复迭代。

如何用Python清洗杂乱数据?预处理完整流程

用Python清洗杂乱数据,核心在于利用Pandas等库,系统性地处理缺失值、重复项、格式不一致及异常值,将原始的“脏数据”转化为可分析、高质量的结构化数据。这通常是数据项目中最耗时但也最关键的第一步,它直接决定了后续分析和模型训练的可靠性。

如何用Python清洗杂乱数据?预处理完整流程

解决方案

数据清洗是一个迭代的过程,没有一劳二的万能公式,更像是一场侦探游戏,需要你深入了解数据本身。我的做法通常是:先加载数据并进行初步探索,用info()describe()head()isnull().sum()这些方法快速摸清家底。然后,针对性地处理缺失值,决定是填充、删除还是插值。紧接着,我会检查并移除重复项,统一数据格式,比如日期、文本大小写,确保数据类型正确无误。最后,处理那些“不合群”的异常值,并进行一些逻辑校验,确保数据在业务层面上也是合理的。这个过程往往不是线性的,可能需要反复检查和调整。

数据清洗,到底洗什么?——识别常见脏数据类型

说起数据清洗,很多人第一反应就是“把数据弄干净”,但“干净”到底意味着什么?我个人的理解是,让数据变得“可用”且“可靠”。这其中要处理的“脏”东西,大致有这么几类:

如何用Python清洗杂乱数据?预处理完整流程
  1. 缺失值 (Missing Values):这是最常见的。数据采集中断、用户未填写、系统错误……各种原因都可能导致数据字段为空。它们可能是NaNNone,甚至是空字符串或特定的占位符(比如NA-)。如果直接拿去分析,轻则报错,重则结果偏差。
  2. 重复值 (Duplicate Values):有时候,同样的数据记录会被不小心录入多次。比如,一个用户注册了两次,或者同一笔交易被记录了两遍。这些重复项会虚增你的数据集大小,扭曲统计结果。
  3. 格式不一致 (Inconsistent Formats):这尤其体现在文本和日期字段上。比如,表示性别的有“男”、“M”、“male”,日期的有“2023-01-01”、“01/01/2023”、“Jan 1, 2023”。这些看似相同的信息,在计算机眼里却是完全不同的字符串,直接影响聚合和比较。
  4. 数据类型错误 (Incorrect Data Types):数字被当作字符串存储,日期被当作普通对象。这会导致你无法进行数值计算或日期时间操作,甚至排序都可能出错。
  5. 异常值/离群点 (Outliers):某些数据点的值远远偏离了大多数数据。它们可能是录入错误(比如年龄写成了200岁),也可能是真实但极端的情况(比如某个用户消费额是天文数字)。处理不好,它们会严重拉高或拉低平均值,影响模型表现。
  6. 逻辑错误 (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实战策略

处理缺失值,这真的是数据清洗里最让人头疼也最考验经验的一环。究竟是删除含有缺失值的行或列,还是想办法填充它们?这没有绝对的答案,完全取决于你的数据量、缺失值的比例、缺失模式以及你后续的分析目的。

如何用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()可能返回多个众数,通常取第一个。
  • 前向填充 (Forward Fill, ffill)df['列名'].fillna(method='ffill')。用前一个有效值填充当前缺失值。常用于时间序列数据,假设缺失值与前一个值相同。
  • 后向填充 (Backward Fill, bfill)df['列名'].fillna(method='bfill')。用后一个有效值填充当前缺失值。
  • 插值 (Interpolation)df['列名'].interpolate()。根据已知值的趋势来推断缺失值,适用于数值型数据,特别是时间序列或有序数据。支持多种插值方法,如线性、多项式等。

我的经验是,对于数值型缺失值,如果分布偏态,中位数往往是比均值更稳妥的选择。对于类别型数据,众数填充通常比较合理。对于时间序列,ffillbfill,甚至更复杂的序列模型插值会更合适。没有哪种方法是完美的,选择最合适的,需要你对数据有足够的了解和判断。

# 处理缺失值示例
# 复制一份数据,避免影响后续操作
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']):只考虑特定列的组合来判断是否重复。比如,你可能认为只要IDName都一样,就是重复记录。
    • 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显示作者元数据技巧用CSS显示作者元数据技巧
上一篇
用CSS显示作者元数据技巧
全屏视频背景优化4种方法详解
下一篇
全屏视频背景优化4种方法详解
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    542次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    511次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    498次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    484次学习
查看更多
AI推荐
  • 扣子空间(Coze Space):字节跳动通用AI Agent平台深度解析与应用
    扣子-Space(扣子空间)
    深入了解字节跳动推出的通用型AI Agent平台——扣子空间(Coze Space)。探索其双模式协作、强大的任务自动化、丰富的插件集成及豆包1.5模型技术支撑,覆盖办公、学习、生活等多元应用场景,提升您的AI协作效率。
    11次使用
  • 蛙蛙写作:AI智能写作助手,提升创作效率与质量
    蛙蛙写作
    蛙蛙写作是一款国内领先的AI写作助手,专为内容创作者设计,提供续写、润色、扩写、改写等服务,覆盖小说创作、学术教育、自媒体营销、办公文档等多种场景。
    12次使用
  • AI代码助手:Amazon CodeWhisperer,高效安全的代码生成工具
    CodeWhisperer
    Amazon CodeWhisperer,一款AI代码生成工具,助您高效编写代码。支持多种语言和IDE,提供智能代码建议、安全扫描,加速开发流程。
    30次使用
  • 畅图AI:AI原生智能图表工具 | 零门槛生成与高效团队协作
    畅图AI
    探索畅图AI:领先的AI原生图表工具,告别绘图门槛。AI智能生成思维导图、流程图等多种图表,支持多模态解析、智能转换与高效团队协作。免费试用,提升效率!
    54次使用
  • TextIn智能文字识别:高效文档处理,助力企业数字化转型
    TextIn智能文字识别平台
    TextIn智能文字识别平台,提供OCR、文档解析及NLP技术,实现文档采集、分类、信息抽取及智能审核全流程自动化。降低90%人工审核成本,提升企业效率。
    65次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码