特征工程:编码、缩放与缺失值处理
一句话定义
特征工程把原始数据转化为模型可用的数值表示:数值缩放统一量纲、类别编码离散取值、缺失值填补或标记、并通过变换与构造注入领域知识——「数据和特征决定上限,模型只是在逼近这个上限」。
为什么重要
结构化数据任务中,好的特征常常比换模型带来更大的提升:对数变换能把回归从「完全不可用」变「可用」,目标编码能把高基数类别从灾难变利器。特征工程还是数据泄漏的高发区(kp-025),处理手法必须与 Pipeline 纪律绑定学习。
前置知识
线性回归与最小二乘法 的线性模型视角(为什么尺度与线性假设重要);基本统计量。
核心概念
- 标准化:z = x - μσ,零均值单位方差,线性模型/SVM/聚类的默认前置。
- 归一化(Min-Max):x' = x - xminxmax - xmin 压到 [0,1],对离群值脆弱。
- 独热编码(One-Hot):类别 → 二元向量;基数高时维度爆炸且对树模型不友好。
- 序数编码:类别 → 整数;只对真有序类别合法。
- 目标编码:类别 → 该类目标均值(如 类内正例 + m · p全局类内样本 + m 的平滑版),强大但极易泄漏,必须折内拟合。
- 缺失值策略:删除(缺失少且随机)、统计填充(中位数/众数)、模型填充(KNN/迭代)、缺失指示特征(「缺失本身有信息」)。
- 变换与构造:对数/Box-Cox 压重尾、分箱注入非线性、交互特征(x1 x2)、日期拆解(星期/节假日)、领域比率特征。
直观类比
做菜备料:模型是火候(算法),特征是刀工与配菜。同一块牛肉(原始数据),切法(缩放)、去筋(缺失处理)、腌料(编码)不同,成品天差地别。备料环节还最容易出现「把生熟混装」(泄漏)的失误——上一道工序的半成品直接进了摆盘。
原理与机制
为什么线性模型必须缩放:损失面的条件数由特征尺度比决定,尺度悬殊时梯度下降之字形、正则化不公平(大量纲系数天然小、被罚得少,kp-008)、基于距离的 SVM/KNN/K-means 被大量纲特征统治。树模型按阈值切分、与单调变换无关,故不需要缩放——「要不要缩放」本质是「模型是否依赖距离/梯度/正则」。
编码选择的权衡:one-hot 保留无序性但维度膨胀(10 万个城市 → 10 万列),线性模型可消化、树模型会被高基数稀释分裂收益;目标编码把高基数压成一列且带排序信息,但「类内均值含标签」使它成为泄漏重灾区,标准解法是交叉折叠目标编码(训练分 fold,每 fold 用其他 fold 的目标统计)+ 平滑先验。缺失值机制先行:MCAR(完全随机)可删可填,MAR/非随机缺失(如「高收入不填收入」)必须用缺失指示保留信号——「缺失模式」本身常是最强特征。
公式与推导
平滑目标编码的收缩公式(贝叶斯先验收缩思想):
nc 为类别 c 的样本数、m 为平滑强度。推导逻辑:把「类内均值」当样本证据、「全局均值」当先验均值,样本越少越收缩向先验——小类别的编码不再由三两条样本的偶然均值决定,兼防过拟合与泄漏放大。
图示
原始列 处理后
收入: 3k~9000万 ──log──▶ 8.0~18.3 (重尾→近正态)
城市: [北京,上海,…10万类] ─目标编码─▶ 0.013, 0.087,…(1列)
年龄: [23, null, 51] ─中位数+指示─▶ 23, 0, 51 / 指示列: [0,1,0]
等级: [低,中,高] ─序数编码─▶ 0, 1, 2(真有序才合法)实例或案例
ColumnTransformer 对不同列施加不同处理(与 kp-029 的 Pipeline 打通):
import numpy as np, pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
df = pd.DataFrame({
"收入": [5e3, np.nan, 2e5, 8e3], "年龄": [25, 40, np.nan, 33],
"城市": ["北京", "上海", "北京", "广州"], "是否购买": [0, 1, 1, 0]})
num, cat = ["收入", "年龄"], ["城市"]
pre = ColumnTransformer([
("数值", Pipeline([("填充", SimpleImputer(strategy="median")),
("缩放", StandardScaler())]), num),
("类别", OneHotEncoder(handle_unknown="ignore"), cat),
])
print(pd.DataFrame(pre.fit_transform(df.drop(columns="是否购买")),
columns=pre.get_feature_names_out()).round(2))常见误区
- 全量数据上拟合编码器/填充值:预处理泄漏的标准形态,必须进 Pipeline 逐折拟合(kp-025)。
- 对树模型做无谓的缩放与 one-hot 大类:树按阈值分裂,缩放无效;高基数 one-hot 稀释分裂且重要性失真(kp-014),树系用序数/目标编码更合适。
- 用训练集不存在取值的编码直接上线:新类别导致崩溃,
handle_unknown="ignore"或哈希兜底。 - 无脑填充均值:重尾分布中位数更稳;非随机缺失时填充+指示列双管齐下。
与其他知识点的关系
正则化:岭回归、Lasso 与弹性网 的正则化公平性依赖缩放;数据泄漏与常见评估陷阱 的纪律约束本节每个变换;特征选择:过滤、包裹与嵌入方法 在工程后的特征上做选择;端到端机器学习工作流与 Pipeline 把本节组件组装成可复现工作流;深度学习导览:边界与去向 的深度学习以「表示学习」替代人工构造,是本节的对照面。
自测题
- 为什么线性模型需要缩放而树模型不需要?
- 要点:线性模型受尺度影响的有梯度条件数、距离(无)与正则公平性;树只比较阈值序、与单调变换无关。
- 高基数类别特征的两难与解法?
- 要点:one-hot 维度爆炸且对树不友好,目标编码强大但泄漏高危;解法是折内目标编码 + 平滑收缩(m 先验)。
- 什么情况下「缺失值本身」是重要特征?
- 要点:缺失非随机(MAR/MNAR)时缺失与目标相关,如高收入者不填收入;补一列缺失指示保留该信号。
延伸阅读
Zheng & Casari《Feature Engineering for Machine Learning》(2018);周志华《机器学习》第 11 章特征选择部分。