数据清洗的核心步骤与基础方法
第一步:评估与诊断
在动手清洗前,先全面了解数据的状况。

- 查看数据概览:
df.head(),df.tail(),df.sample():查看首尾和随机样本。df.info():查看列的数据类型、非空值数量,快速发现类型错误和大量缺失。df.describe():查看数值型列的统计摘要(均值、标准差、分位数等),初步感知异常值。
- 检查数据问题:
- 缺失值:
df.isnull().sum()统计每列缺失数量。 - 重复值:
df.duplicated().sum()检查完全重复的行。 - 不一致性:
- 大小写:
‘New York’vs‘new york’ - 格式:
‘2023-01-01’vs‘01/01/2023’ - 拼写/缩写:
‘USA’vs‘U.S.A’vs‘United States’ - 分类值不一致:性别列出现
‘M’, ‘F’, ‘Male’, ‘female’
- 大小写:
- 异常值:通过描述性统计、箱线图或散点图发现明显偏离群体的值。
- 缺失值:
第二步:处理缺失值
根据缺失原因、比例和业务逻辑选择策略。 | 方法 | 说明 | 适用场景 | | :--- | :--- | :--- | | 删除 | 删除缺失值所在的行或列。 | 缺失比例很高(如>50%),或缺失行对分析不重要。慎用,易损失信息。 | | 填充 | 用特定值替换。 | 缺失比例较低,且需要保留所有数据。 | | | 固定值填充:用0、‘Unknown’、中位数、众数等。 | 简单快捷,适用于数值或分类。 | | | 前向/后向填充:用前一个或后一个有效值填充。 | 时间序列数据。 | | | 统计量填充:用均值、中位数(数值)、众数(分类)填充。 | 数据分布较均匀时。 | | | 插值法:线性、多项式插值。 | 有序数据(如时间序列)。 | | | * 模型预测填充:用其他列建立模型预测缺失值。 | 数据量大,关系复杂时。 | | 保留 | 将缺失视为一种特殊状态。 | 缺失本身可能有意义(如“用户未填写收入”)。 |
第三步:处理异常值
异常值可能是错误,也可能是重要信息(如欺诈交易)。
- 检测方法:
- 标准差法:假设数据服从正态分布,超出均值±3倍标准差的范围视为异常。
- 箱线图法:将小于
Q1 - 1.5*IQR或大于Q3 + 1.5*IQR的值视为异常(最常用)。 - 业务规则法:根据领域知识判断(如年龄>200为异常)。
- 处理方法:
- 删除:确认为错误数据时。
- 修正:根据上下文修正为合理值(类似于缺失值填充)。
- 替换:用上下限值(如
Q1 - 1.5*IQR)进行截断。 - 分箱:将连续值离散化,可减弱异常值影响。
- 保留:在分析时注明,或使用对异常值不敏感的模型/统计量。
第四步:处理不一致与格式化
- 文本数据:
- 大小写统一:
df[‘col’] = df[‘col’].str.lower() - 去除空格:
df[‘col’] = df[‘col’].str.strip() - 统一格式:如日期格式
pd.to_datetime(df[‘date’], format=‘%Y/%m/%d’) - 映射替换:
df[‘gender’].replace({‘M’:‘male’, ‘F’:‘female’}) - 正则表达式:提取、替换复杂模式的文本。
- 大小写统一:
- 分类数据:
- 合并同类项:将含义相同但表述不同的值合并。
第五步:处理重复值
- 完全重复:
df.drop_duplicates(inplace=True) - 关键字段重复:根据业务逻辑,判断哪些列组合应唯一(如用户ID+下单时间),然后删除重复。
- 判断保留哪条:可能需要根据时间戳或其他列排序后保留最新/最旧的一条。
第六步:类型转换与衍生
- 数据类型转换:
- 将数字存储为字符串的列转为数值型:
pd.to_numeric(df[‘col’], errors=‘coerce’) - 将分类文本转为
category类型以节省内存。 - 将布尔值转换为
0/1。
- 将数字存储为字符串的列转为数值型:
- 创建衍生特征:
- 从日期中提取年、月、季度、星期几。
- 将连续值分箱(如年龄分为“青年”、“中年”、“老年”)。
- 组合多个特征(如“面积”/“人口”得到“人口密度”)。
第七步:验证与保存
- 验证清洗效果:
- 再次运行
df.info(),df.describe(),检查缺失、类型、统计量。 - 进行业务逻辑检查(如年龄不应为负,销售额与单价*数量应一致)。
- 再次运行
- 保存清洗后数据:
- 保存为新文件,永远保留原始数据备份。
常用工具与代码示例(Python Pandas)
import pandas as pd
import numpy as np
df = pd.read_csv(‘dirty_data.csv’)
# 2. 诊断
print(df.info())
print(df.isnull().sum())
# 3. 处理缺失值 - 填充
df[‘income’].fillna(df[‘income’].median(), inplace=True) # 中位数填充
df[‘education’].fillna(‘Unknown’, inplace=True) # 固定值填充
# 4. 处理异常值 - 截断 (箱线图法)
Q1 = df[‘salary’].quantile(0.25)
Q3 = df[‘salary’].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
df[‘salary’] = df[‘salary’].clip(lower_bound, upper_bound) # 将超出部分截断到边界
# 5. 处理不一致性
df[‘city’] = df[‘city’].str.strip().str.title() # 去除空格并首字母大写
df[‘gender’] = df[‘gender’].replace({‘M’:‘Male’, ‘F’:‘Female’, ‘m’:‘Male’})
# 6. 处理重复值
df.drop_duplicates(subset=[‘user_id’, ‘order_time’], keep=‘last’, inplace=True)
# 7. 类型转换
df[‘signup_date’] = pd.to_datetime(df[‘signup_date’])
df[‘age’] = pd.to_numeric(df[‘age’], errors=‘coerce’)
# 8. 衍生特征
df[‘signup_year’] = df[‘signup_date’].dt.year
df[‘age_group’] = pd.cut(df[‘age’], bins=[0, 18, 35, 60, 100], labels=[‘少年’, ‘青年’, ‘中年’, ‘老年’])
# 9. 保存
df.to_csv(‘cleaned_data.csv’, index=False)
核心原则与建议
- 先备份,再操作:永远保留一份原始数据的副本。
- 记录清洗步骤:创建数据清洗日志或脚本,确保过程可复现、可审计。
- 理解业务逻辑:很多清洗决策(如如何处理异常值)需要结合领域知识,不能仅依赖统计方法。
- 迭代进行:数据清洗不是一步到位的线性过程,常常需要“诊断-处理-再诊断”的循环。
- 平衡效率与质量:对于大型数据集,有时需要在完美清洗和计算效率之间做出权衡。
掌握这些基础方法后,您就能应对80%以上的常见数据清洗任务,更复杂的情况(如文本清洗、非结构化数据清洗)则需要在基础上深入学习特定技术。