数据预处理 = 深度清洗 → 划分 → 特征工程 → 标准化(防数据泄漏)。脏数据喂不出好模型,这一步决定训练效率和上限。
从"脏数据集"到"模型可吃的干净数据"——清洗 → 划分 → 特征工程 → 标准化(防数据泄漏)
train_test_split + stratify;时间序列按先后顺序切;有分组(user_id)用 GroupShuffleSplit质检初筛是"看病发报告",深度清洗是"动手治病"。同一个问题(缺失值、异常值),质检初筛阶段只统计不修改,深度清洗阶段才根据决策落地处理。
| 模态 | 🗑️ 初筛直接扔(不可救药的真垃圾) | 📝 初筛统计但留着(可修复的,交给深度清洗) |
|---|---|---|
| 📊 表格 | 整行完全空白 / 完全重复的副本 / 文件损坏打不开 / 全部字段只有 NaN | value 列 8% 缺失 / 12 个异常值 / 日期格式不统一(5种写法)/ 分类值有别名 |
| 🖼️ 图像 | 文件损坏 / 全黑 / 全白 / 分辨率 < 32×32 / 完全重复像素 | EXIF 方向标记导致歪 90° / 有高斯噪声 / CLAHE 才能看清的暗光 / 轻微压缩伪影 |
| 📝 文本 | 空字符串 / 完全重复的评论(刷量)/ 无法解码的乱码 | HTML 标签残留 / URL 和 @用户名 / Emoji 要不要留 / 繁简混杂 |
| 🎵 音频 | 文件损坏 / 时长 < 0.1 秒 / 全静音 / 全部段完全相同 | 首尾各 2 秒静音 / 空调底噪 -35dB / 采样率 8kHz 和 16kHz 混杂 / 双声道要转单声道 |
| 📈 时序 | 文件损坏 / 全部只有一个值 / 只有 2 个数据点 / 时间戳乱序且无法还原 | value 列 5% 缺失(但不是全空)/ 秒级分钟级混杂 / 有 3 个尖峰异常 / 方差不平稳 |
点击切换不同数据类型,查看各自的清洗重点和代码示例
点击任意一行查看具体清洗方案和代码:
import pandas as pd import numpy as np # ═══════════════════════════════════════════ # 一键运行:完整深度清洗 Pipeline # 覆盖 4 张卡片:缺失值 · 盖帽 · 格式统一 · 去重 # 依赖:pip install pandas numpy scikit-learn # ═══════════════════════════════════════════ # ====== Step 0: 读取数据 ====== # 🎯 先明确标签列(监督学习必须) TARGET = "churn" df = pd.read_csv("dirty_users.csv") # ====== Step 1: 加载 + 去重 ====== df.drop_duplicates(inplace=True) # ====== Step 2: 标签优先清洗(标签绝对不能瞎填!)====== # 2a. 格式统一:是/否/Yes/No → 1/0 df[TARGET] = df[TARGET].replace({"是":1, "否":0, "Yes":1, "No":0}) # 2b. 非法值 + 缺失 → 直接删行 df = df[df[TARGET].isin([0, 1])] df.dropna(subset=[TARGET], inplace=True) # ====== Step 3: 特征列缺失值智能填充(跳过标签列)====== feature_cols = [c for c in df.columns if c != TARGET] for col in feature_cols: rate = df[col].isna().mean() if rate < 0.05: df.dropna(subset=[col], inplace=True) elif rate < 0.3: fill = df[col].median() if df[col].dtype != "object" \ else df[col].mode()[0] df[col].fillna(fill, inplace=True) else: df.drop(columns=[col], inplace=True) # ====== Step 4: 特征异常值盖帽(跳过标签列)====== for col in df.select_dtypes(include=[np.number]).columns: if col == TARGET: continue Q1, Q3 = df[col].quantile(0.25), df[col].quantile(0.75) IQR = Q3 - Q1 df[col] = df[col].clip(Q1-1.5*IQR, Q3+1.5*IQR) df["age"] = df["age"].clip(0, 120) # ====== Step 5: 输出干净数据 ====== df.to_csv("clean_users.csv", index=False)
# ❌ 错误:全量 fit 会泄漏 imputer = KNNImputer() df_all = imputer.fit_transform(df) # test 的信息被用来填充 train # ✅ 正确:先划分,再在 train 上 fit X_train, X_test, y_train, y_test = train_test_split(df, label) imputer = KNNImputer() X_train_clean = imputer.fit_transform(X_train) # 只用 train 计算 X_test_clean = imputer.transform(X_test) # 用 train 的结果转换 test
关键原则:**测试集必须"没见过"**——调参在验证集上做,测试集只用于最终评估。不能把同一数据同时放进训练和测试集。
from sklearn.model_selection import train_test_split # 两次分割:先切出测试集,再切验证集 X_temp, X_test, y_temp, y_test = train_test_split( X, y, test_size=0.15, random_state=42, stratify=y # 分层抽样!保证类别比例一致 ) X_train, X_val, y_train, y_val = train_test_split( X_temp, y_temp, test_size=0.1765, random_state=42, stratify=y_temp ) # 0.85 * 0.8235 ≈ 0.70 (训练集 70%) # 0.85 * 0.1765 ≈ 0.15 (验证集 15%)
"数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限而已" — 特征工程是投入产出比最高的环节
为什么要做?不同特征量纲差异大(如年龄 18-80 vs 收入 3k-500k),模型会被大数值特征"绑架"。树形模型(XGBoost/随机森林)对尺度不敏感,但线性模型、神经网络、SVM 必须做。
from sklearn.preprocessing import MinMaxScaler scaler = MinMaxScaler() X_train_s = scaler.fit_transform(X_train) X_test_s = scaler.transform(X_test) # 用训练集的 min/max