数据清洗与预处理实战指南:从脏数据到可用资产的具体步骤与避坑技巧

先定位脏数据:常见的5种“数据垃圾”类型

做数据清洗前,得先搞清楚你要处理的“垃圾”是什么——毕竟对症下药才有效。我总结了工作中最常遇到的5种脏数据类型,你可以对照着自己的数据盘一盘:

数据清洗与预处理实战指南:从脏数据到可用资产的具体步骤与避坑技巧

  • 重复值:同一数据条目多次出现(比如电商订单系统bug导致同一笔订单被录入3次);
  • 缺失值:字段为空或未记录(比如用户注册时跳过“年龄”字段,导致该列有10%的空值);
  • 异常值:明显偏离正常范围的值(比如用户身高填了1000cm,或者收入填了100万元但职业是学生);
  • 不一致值:同一字段格式或内容矛盾(比如“性别”列同时有“男”“Male”“1”三种表达,“日期”列有“2025-08-24”“25/08/24”两种格式);
  • 无效值:不符合业务规则的值(比如“手机号”列有10位或12位数字,“学历”列出现“幼儿园”但用户年龄是30岁)。

小互动:你最近处理的数据里,哪类脏数据占比最高?评论区告诉我,我帮你出主意~

缺失值处理:别乱填充!3种场景对应的精准方法

缺失值是最常见的问题,但不是所有缺失值都需要“补”——填错了反而会误导分析。我整理了3种缺失场景对应的处理策略,直接套就行:

缺失类型 处理方法 适用场景
完全随机缺失(MAR) 删除行/列 缺失比例<5%,且缺失值不影响样本代表性(比如1000条数据里50条缺失年龄)
非随机缺失(MNAR) 均值/中位数填充 缺失值与其他字段无关(比如用户年龄缺失,但缺失原因是“懒得填”,不是因为收入高)
系统性缺失 模型预测填充 缺失值与其他字段相关(比如收入缺失,但可以通过“职业”“年龄”“消费记录”预测)

代码示例:用随机森林预测填充收入缺失值
假设你有一份用户数据,“收入”列有20%的缺失,且缺失值与“年龄”“职业”“消费金额”相关,用Python的RandomForestRegressor预测填充:

import pandas as pd
from sklearn.ensemble import RandomForestRegressor

# 读取数据
df = pd.read_csv('user_data.csv')

# 分离特征(无缺失)和目标(有缺失)
X = df.drop(['income', 'user_id'], axis=1).select_dtypes(include=['number'])  # 特征:年龄、消费金额等
y = df['income']  # 目标:收入

# 训练模型(用有收入的样本)
model = RandomForestRegressor(n_estimators=100, random_state=42)
model.fit(X[y.notnull()], y[y.notnull()])

# 预测缺失值并填充
df.loc[y.isnull(), 'income'] = model.predict(X[y.isnull()])

重复值去除:别只删!先搞懂“真重复”和“假重复”的区别

很多人处理重复值的第一反应是“删删删”,但有些“重复”是假的——比如同一用户在不同时间下了两笔订单,订单号不同但用户ID相同,这不是重复值,是有效数据。

正确步骤
1. 先定义“重复键”:比如订单数据用“订单ID”当重复键,用户数据用“用户ID”当重复键;
2. 检查重复键是否唯一:如果“订单ID”重复,才是真重复;如果“用户ID”重复但“订单ID”不同,是多笔订单,不能删。

代码示例:用Pandas去除真重复值

# 读取电商订单数据
df = pd.read_csv('order_data.csv')

# 定义重复键:订单ID
duplicate_mask = df.duplicated(subset=['order_id'])

# 去除重复值(保留第一笔订单)
df_clean = df[~duplicate_mask]

print(f'原数据行数:{len(df)},清洗后行数:{len(df_clean)}')

异常值检测:不是“删除”这么简单!3种方法帮你找到“有问题的信号”

异常值不一定是“坏数据”——比如电商里的“高消费用户”,看起来是异常值,但其实是高价值客户。所以处理异常值的关键是先判断“异常的原因”,再决定留还是删。

我常用这3种方法检测异常值,覆盖90%的场景:

1. 箱线图(适用于单变量)

用可视化快速看异常值范围,适合给非技术同事展示:

import seaborn as sns
import matplotlib.pyplot as plt

# 绘制消费金额的箱线图
sns.boxplot(x=df['consume_amount'])
plt.title('消费金额异常值分布')
plt.xlabel('消费金额(元)')
plt.show()

箱线图外的点就是异常值,比如图中消费金额超过5000元的点。

2. Z-score(适用于正态分布数据)

用数学方法计算“偏离程度”,超过3个标准差的视为异常:

from scipy import stats
import numpy as np

# 计算Z-score(仅适用于数值型数据)
z_scores = stats.zscore(df['consume_amount'])
# 标记异常值(Z-score绝对值>3)
df['is_outlier'] = np.where(abs(z_scores) > 3, 1, 0)

print(f'异常值占比:{df["is_outlier"].mean()*100:.2f}%')

3. Isolation Forest(适用于高维数据)

当数据有多个特征(比如“年龄+收入+消费金额”)时,用机器学习模型检测异常值更准确:

from sklearn.ensemble import IsolationForest

# 选择特征:年龄、收入、消费金额
X = df[['age', 'income', 'consume_amount']]

# 训练Isolation Forest模型(contamination=异常值比例,比如1%)
iso = IsolationForest(contamination=0.01, random_state=42)
df['is_outlier'] = iso.fit_predict(X)  # -1=异常值,1=正常

# 查看异常值
outliers = df[df['is_outlier'] == -1]
print(f'异常值数量:{len(outliers)}')

不一致值处理:统一格式!2个工具帮你搞定“混乱字段”

不一致值的核心是“同一个字段,不同表达”,比如“性别”列有“男”“Male”“1”,“日期”列有“2025-08-24”“25/08/24”。处理这类问题,用Pandas的字符串方法和正则表达式就够了。

1. 用replace统一类别字段

比如把“性别”列的不同表达统一为“0”(女)和“1”(男):

# 统一性别字段格式
gender_mapping = {'Male': '1', '男': '1', 'Female': '0', '女': '0', '未知': '2'}
df['gender'] = df['gender'].replace(gender_mapping)

2. 用正则表达式提取有效信息

比如从“联系方式”列中提取纯手机号(11位数字):

# 提取11位纯数字手机号
df['phone'] = df['contact'].str.extract(r'(d{11})')  # 正则表达式:匹配11位数字

避坑提醒:数据清洗中最容易犯的4个错误

我踩过的坑,你别再踩了:

  1. 错误1:为了“完整”填充所有缺失值
    比如用均值填充“收入”缺失值,但高收入用户的缺失会拉低整体均值,导致分析结果偏倚。
  2. 错误2:忽略数据的业务含义
    比如将“性别”列的“未知”填充为“男”,但“未知”可能是“不愿意透露”的用户,直接填充会误导用户分层。
  3. 错误3:跳过数据溯源
    比如重复值可能是系统bug导致的(比如订单系统延迟,同一笔订单被提交2次),直接删除会丢失“系统问题”的信号。
  4. 错误4:用统一方法处理所有异常值
    比如将高消费的异常值删除,但这些用户可能是“高价值客户”,删了会漏掉重要的业务机会。

实战案例:用Python完成电商用户数据清洗

最后用一个真实案例,带你走一遍完整流程:

需求:将电商用户注册数据(user_register.csv)清洗为可用于“用户分层”的干净数据。
数据问题:重复值(5%)、缺失值(年龄:10%,收入:20%)、不一致值(性别:3种表达)、异常值(消费金额:1%)。

步骤1:读取数据

import pandas as pd
df = pd.read_csv('user_register.csv')

步骤2:去除重复值

# 按用户ID去重(保留第一笔注册记录)
df = df.drop_duplicates(subset=['user_id'], keep='first')

步骤3:处理缺失值

# 年龄缺失:用中位数填充(非随机缺失)
df['age'].fillna(df['age'].median(), inplace=True)

# 收入缺失:用随机森林预测填充(系统性缺失)
from sklearn.ensemble import RandomForestRegressor

# 分离特征和目标
X = df.drop(['income', 'user_id'], axis=1).select_dtypes(include=['number'])
y = df['income']

# 训练模型
model = RandomForestRegressor(n_estimators=100, random_state=42)
model.fit(X[y.notnull()], y[y.notnull()])

# 填充缺失值
df.loc[y.isnull(), 'income'] = model.predict(X[y.isnull()])

步骤4:统一不一致值

# 统一性别格式
gender_mapping = {'Male': '1', '男': '1', 'Female': '0', '女': '0'}
df['gender'] = df['gender'].replace(gender_mapping)

步骤5:检测并处理异常值

# 用Isolation Forest检测消费金额异常值(保留高价值用户)
from sklearn.ensemble import IsolationForest

X = df[['age', 'income', 'consume_amount']]
iso = IsolationForest(contamination=0.01, random_state=42)
df['is_outlier'] = iso.fit_predict(X)

# 保留异常值(高消费用户),标记为“高价值”
df['user_level'] = np.where(df['is_outlier'] == -1, '高价值', '普通')

结果:清洗后的数据包含user_id(用户ID)、age(年龄)、gender(性别)、income(收入)、consume_amount(消费金额)、user_level(用户分层)6个字段,完全满足“用户分层”的分析需求。

最后说一句:数据清洗的核心是“适配需求”

很多人觉得数据清洗是“体力活”,但其实清洗的方向取决于你的分析目标——比如做“用户分层”时,“收入”的缺失值需要精准填充;但做“活跃度分析”时,“收入”的缺失值可以直接删除。

记住:数据不是越“干净”越好,而是越“适配需求”越好

原创文章,作者:,如若转载,请注明出处:https://zube.cn/archives/209

(0)