特征工程与实践核心25 分钟kp-027#特征工程#编码#缺失值

特征工程:编码、缩放与缺失值处理

进度

一句话定义

特征工程把原始数据转化为模型可用的数值表示:数值缩放统一量纲、类别编码离散取值、缺失值填补或标记、并通过变换与构造注入领域知识——「数据和特征决定上限,模型只是在逼近这个上限」。

为什么重要

结构化数据任务中,好的特征常常比换模型带来更大的提升:对数变换能把回归从「完全不可用」变「可用」,目标编码能把高基数类别从灾难变利器。特征工程还是数据泄漏的高发区(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/非随机缺失(如「高收入不填收入」)必须用缺失指示保留信号——「缺失模式」本身常是最强特征。

公式与推导

平滑目标编码的收缩公式(贝叶斯先验收缩思想):

enc(c) = nc · ȳc + m · ȳglobalnc + m

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 把本节组件组装成可复现工作流;深度学习导览:边界与去向 的深度学习以「表示学习」替代人工构造,是本节的对照面。

自测题

  1. 为什么线性模型需要缩放而树模型不需要?

- 要点:线性模型受尺度影响的有梯度条件数、距离(无)与正则公平性;树只比较阈值序、与单调变换无关。

  1. 高基数类别特征的两难与解法?

- 要点:one-hot 维度爆炸且对树不友好,目标编码强大但泄漏高危;解法是折内目标编码 + 平滑收缩(m 先验)。

  1. 什么情况下「缺失值本身」是重要特征?

- 要点:缺失非随机(MAR/MNAR)时缺失与目标相关,如高收入者不填收入;补一列缺失指示保留该信号。

延伸阅读

Zheng & Casari《Feature Engineering for Machine Learning》(2018);周志华《机器学习》第 11 章特征选择部分。