6-3缺失值插补与特征衍生(多项式特征)

🎯 教学目标与重难点…

【三维目标】

  • 📚 知识目标
    1. 掌握识别 Pandas 中缺失值(NaN)的方法:isnull()info()
    2. 掌握三种缺失值填充策略:均值填充(数值型)、中位数填充(防异常值)、众数填充(类别型)。
    3. 掌握 SimpleImputerfillna 的使用。
    4. 理解什么是“特征交互”,掌握使用 PolynomialFeatures 生成平方项和交叉项。
  • ⚙️ 能力目标
    1. 能根据数据分布特征(是否有极端值)合理选择均值/中位数插补。
    2. 能利用多项式特征将线性模型“变强”,解决“特征不足”的困境。
  • 💡 素养目标
    1. 树立“数据不完美是常态,预处理就是修修补补”的职业心态。
    2. 体会“特征工程”的魅力——好特征比好算法更重要。

【重点与难点】

  • 🟢 教学重点
    1. 使用 SimpleImputer 进行均值/中位数/众数填充的标准代码模板。
    2. 使用 PolynomialFeatures 生成交互项(如 长×宽 = 面积)的逻辑。
  • 🟡 教学难点
    1. 数据泄露陷阱:填充缺失值时,只能用训练集的统计数据(如均值)去填充测试集,绝对不能先用全部数据算均值再分集(这是作弊)。
    2. 多项式特征会导致特征数量爆炸(维度灾难),需要配合后面的正则化使用。

📌 一、 课程导入(5 分钟)

情景模拟:老师收作业,有人没交怎么办?

假设老师统计全班同学的“身高”和“体重”来做体育成绩预测。 课代表收上来的表格是这样的:

姓名 身高 (cm) 体重 (kg)
小明 170 65
小红 NaN (空) 55
小刚 180 NaN (空)

问题来了

  1. 如果直接把这两行空数据删掉,小红和小刚的训练样本就浪费了(太可惜)。
  2. 如果什么都不填,代码直接报错(机器学习模型不接受空值)。

引出解决方案:我们必须用科学的方法“猜”出一个合理的数填进去。这叫 缺失值插补

第二个问题(预告造武器): 如果我们只有“房间长度”和“房间宽度”预测房价,模型只能看到两个孤立的数。但如果老师告诉你,“长 × 宽 = 面积”,面积才是决定房价的关键!怎么办? 引出解决方案:我们自己动手,用现有的特征相乘,造出新的特征!这叫 生成多项式特征

  • 👨‍🏫 教师活动:展示带空格的表格,提问:“如果让你给小红的体重填个数,你凭直觉填多少?”(学生凭直觉会说平均值 60)。
  • 🧑‍🎓 学生活动:学生积极回应填平均值。老师顺势引出“插补”概念。接着再问“长和宽那个例子”,激发造新特征的兴趣。

利用“收作业”这个职高学生极其熟悉的场景,立刻化解对“缺失值”的恐惧。再用“长×宽”的朴素常识,让学生理解“特征衍生”并非高深数学,而是生活常识。


📖 二、 解决问题过程(一):缺失值插补——填坑的艺术

1. 先体检:如何发现空值(NaN)? 在 Pandas 中,空值显示为 NaN(Not a Number)。

1import pandas as pd
2import numpy as np
3
4df = pd.DataFrame({
5    '身高': [170, np.nan, 180],
6    '体重': [65, 55, np.nan]
7})
8print(df.isnull())  # True 代表是空值
9print(df.info())    # 查看每列有多少非空值

2. 三大插补策略(重点!)

策略 代码 适用场景 优点/缺点
均值填充 SimpleImputer(strategy='mean') 数据分布对称,无明显异常值 简单,但受极端值影响大
中位数填充 SimpleImputer(strategy='median') 数据有明显异常值(如收入) 稳健,不被马云拉偏
众数填充 SimpleImputer(strategy='most_frequent') 类别型数据(如性别、颜色) 填出现次数最多的那个值

3. 核心代码模板(工业级标准) 职高学生必记口诀:先 fit 训练集,再 transform 训练集和测试集!

 1from sklearn.impute import SimpleImputer
 2import numpy as np
 3
 4# 模拟数据(训练集有缺失)
 5X_train = np.array([[170, 65], [np.nan, 55], [180, np.nan]])
 6X_test = np.array([[175, np.nan]])  # 测试集也有缺失
 7
 8# 1. 创建插补器(以均值策略为例)
 9imputer = SimpleImputer(strategy='mean')
10
11# 2. 重点!imputer.fit(X_train) 只计算训练集的均值
12imputer.fit(X_train)
13
14# 3. 用训练集的均值去转换训练集和测试集
15X_train_filled = imputer.transform(X_train)
16X_test_filled = imputer.transform(X_test)
17
18print("训练集均值:", imputer.statistics_)  # 均值只来自训练集!
19print("填充后的测试集:", X_test_filled)

⚠️ 血泪警示(数据泄露)绝对禁止先填充全部数据再划分训练集/测试集!那样测试集的均值会偷偷影响训练集,导致模型评估虚高(作弊)!

  • 👨‍🏫 教师活动
    1. 重点在黑板强调 fittransform 的分离。
    2. 演示如果先填充再分集(错误做法)和先分集再填充(正确做法)的区别。
  • 🧑‍🎓 学生活动
    1. 学生观察 imputer.statistics_ 的输出,验证它只算了训练集的平均值。
    2. 学生讨论:“如果测试集有一个缺失值,但训练集没有缺失值,fit 还能用吗?”(答案:能用,因为 fit 算出均值存着,随时可用)。

代码模板直接给,不绕弯子。重点不是讲统计学,而是讲 “防止数据泄露” 的工程红线,这是职高生进入企业最容易挨骂的细节。


💻 三、 解决问题过程(二):生成多项式特征——无中生有造武器

1. 核心思想:特征之间的“乘法”和“平方” 线性回归模型只能看到:\( y = w_1 x_1 + w_2 x_2 + b \)。 如果真实规律是 \( y = x_1^2 + x_1 x_2 \)(比如房价受“面积=长×宽”影响),线性模型就抓瞎了。

解决方案:我们手动给数据增加几列,把 \( x_1^2 \) 和 \( x_1 x_2 \) 当作新的特征喂给模型。

2. 生活化类比(厨房调料) 只有“盐”(\( x_1 \))和“醋”(\( x_2 \))两种调料,味道太单调。 多项式特征就是允许你把它们混合成“糖醋汁”(\( x_1 x_2 \))和“超级咸”(\( x_1^2 \)),菜品的味道(模型效果)瞬间上升一个档次!

3. 代码实战(PolynomialFeatures)

 1from sklearn.preprocessing import PolynomialFeatures
 2import numpy as np
 3
 4# 原始数据:长 和 宽(2个特征)
 5X = np.array([[2, 3], [4, 5], [1, 1]])
 6
 7# 创建多项式生成器(最高次为2)
 8poly = PolynomialFeatures(degree=2, include_bias=False)
 9X_poly = poly.fit_transform(X)
10
11print("原始特征 (2列):\n", X)
12print("转换后特征 (5列):\n", X_poly)
13print("特征名称:", poly.get_feature_names_out(['长', '宽']))

运行结果解读: 原始 [长, 宽] 变成了 [长, 宽, 长^2, 长×宽, 宽^2]

长² 长×宽 宽²
2 3 2 3 4 6 9

4. 适用场景与警告

  • 适合:特征极少(比如只有2-3个特征),且你怀疑它们之间存在交互关系(如面积、加速度)。
  • 警告(铁律)degree=2 会让特征数从 n 变成 n + n + ...(爆炸增长)。如果原始有 10 个特征,degree=3 会变成上百个特征!特征太多容易过拟合(下一章正则化会解决这个问题)。
  • 👨‍🏫 教师活动
    1. 在黑板上演算:(2,3) 是如何变成 (2,3,4,6,9) 的,告诉学生这就是乘法分配律。
    2. 强调 include_bias=False(不要那列全是1的常数项,交给模型自己去算截距)。
  • 🧑‍🎓 学生活动
    1. 学生手动填写:如果原始是 (1, 5),转换后是多少?答案:(1, 5, 1, 5, 25)
    2. 学生运行 X_poly.shape 观察形状变化,直观感受特征变多了。

用“糖醋汁”的比喻让“交叉项”变得极其通俗。重点让学生感受 fit_transform 后矩阵“变胖”了,并记住“多项式能治线性模型的傻病”。


✍️ 四、 解决问题过程(三):课堂实战——“二手房价预测”预处理流水线

📝 任务一:综合实战(缺失值填充 + 造特征)…

背景与题目: 你拿到一份迷你房价数据集(单位:万元),包含:

  • 特征:面积(m²)卧室数量房龄(年)
  • 注意:数据里有缺失值,且面积和卧室数量可能组合成 “总房间使用面积 × 数量” 的潜力。
 1import pandas as pd
 2import numpy as np
 3from sklearn.impute import SimpleImputer
 4from sklearn.preprocessing import PolynomialFeatures
 5
 6# 原始数据(6条训练样本,2条测试样本)
 7X_train = np.array([
 8    [80, 2, 5],
 9    [np.nan, 3, 10],   # 面积缺失
10    [120, 4, np.nan],  # 房龄缺失
11    [60, 1, 2],
12    [100, 3, 15],
13    [np.nan, 2, 8]     # 面积缺失
14])
15X_test = np.array([
16    [90, np.nan, 3],   # 卧室数量缺失
17    [110, 3, np.nan]   # 房龄缺失
18])

请完成以下操作

  1. 缺失值插补:使用 SimpleImputer'median'(中位数)策略填充所有缺失值(因为房价数据可能有异常值,中位数更稳)。
  2. 生成多项式特征:使用 PolynomialFeatures(degree=2, include_bias=False) 将填充后的 (面积, 卧室数, 房龄) 转换成包含平方和交叉项的新特征。
  3. 观察变化:打印转换后的训练集形状,看看从几列变成了几列?
🔍 查看参考代码与解析…
 1import numpy as np
 2import pandas as pd
 3from sklearn.impute import SimpleImputer
 4from sklearn.preprocessing import PolynomialFeatures
 5
 6# 1. 原始数据
 7X_train = np.array([
 8    [80, 2, 5],
 9    [np.nan, 3, 10],
10    [120, 4, np.nan],
11    [60, 1, 2],
12    [100, 3, 15],
13    [np.nan, 2, 8]
14])
15X_test = np.array([
16    [90, np.nan, 3],
17    [110, 3, np.nan]
18])
19
20# 2. 创建中位数插补器并拟合训练集
21imputer = SimpleImputer(strategy='median')
22imputer.fit(X_train)
23print("各列中位数(仅基于训练集):", imputer.statistics_)
24
25# 3. 填充数据
26X_train_filled = imputer.transform(X_train)
27X_test_filled = imputer.transform(X_test)
28print("\n填充后的测试集:\n", X_test_filled)
29
30# 4. 生成多项式特征(degree=2)
31poly = PolynomialFeatures(degree=2, include_bias=False)
32X_train_poly = poly.fit_transform(X_train_filled)
33X_test_poly = poly.transform(X_test_filled)
34
35print("\n原始训练集形状:", X_train_filled.shape)  # (6, 3)
36print("多项式转换后形状:", X_train_poly.shape)  # (6, 9) -> 3原始 + 3平方 + 3交叉项 = 9
37print("新增的特征名称:", poly.get_feature_names_out(['面积', '卧室', '房龄']))

解析:通过中位数填充,我们保住了所有样本。通过多项式,我们把 3 个特征变成了 9 个,线性模型的能力大大增强!


📝 五、 课堂小结(5 分钟)

flowchart LR
    root["🧹 预处理(三) 填坑与造武器"]

    subgraph C1 ["🕳️ 缺失值插补"]
        direction TB
        A1["检测: isnull() / info()"]
        A2["均值 (mean) -> 无异常值"]
        A3["中位数 (median) -> 有异常值"]
        A4["众数 (most_frequent) -> 类别型"]
        A5["⚠️ 铁律:只 fit 训练集!"]
    end

    subgraph C2 ["🔧 生成多项式特征"]
        direction TB
        B1["目的:捕捉交互与非线性"]
        B2["公式: [a,b] -> [a,b,a²,ab,b²]"]
        B3["代码: PolynomialFeatures"]
        B4["⚠️ 警告:特征爆炸,易过拟合"]
    end

    root --> C1
    root --> C2

    style root fill:#4b6cb7,stroke:#253b6e,color:#fff
    style C1 fill:#e3f2fd,stroke:#2196f3
    style C2 fill:#fff3e0,stroke:#ff9800

✏️ 随堂检测与互动练习

点击展开:随堂测试题(带解析)

一、 单选题(数据泄露判断)

  1. 以下哪种预处理操作可能导致严重的数据泄露(作弊)
  • A. 在 train_test_split 之后,只对训练集使用 StandardScaler.fit()
  • B. 在 train_test_split 之前,使用 SimpleImputer(strategy='mean') 填充全部数据的缺失值,然后再划分训练集和测试集
  • C. 在 train_test_split 之后,对训练集和测试集分别使用各自的均值进行填充
  • D. 在 train_test_split 之后,只对测试集使用 StandardScaler.transform()
【答案】

【解析】B。如果先填充全部数据再分集,训练集在计算时已经“偷看”了测试集的均值,导致模型评估结果虚高。正确做法是先分集,再 fit 训练集,transform 两者。

  1. 某数据有特征 [收入],其中 99 个人月薪 5000,1 个人月薪 10 亿(马云)。此时填充缺失值最稳健的策略是?
  • A. 均值填充(Mean)
  • B. 中位数填充(Median)
  • C. 众数填充(Most Frequent)
  • D. 直接删除缺失值
【答案】

【解析】B。因为存在极端异常值(10亿),均值会被拉得极高,无法代表普通人的水平。中位数不受极端值影响,最稳健。

二、 代码填空题

题目:现有训练集 X_train(含缺失值),请补充代码完成多项式特征生成,并将 degree 设置为 2。

1from sklearn.preprocessing import PolynomialFeatures
2
3# 假设 X_train 已经填充好缺失值
4
5# 创建多项式特征生成器,不要偏置列
6poly = _________________________________
7X_train_poly = _________________________________
【答案】
  1. PolynomialFeatures(degree=2, include_bias=False)
  2. poly.fit_transform(X_train)

📮 六、 课后作业与拓展

📮 课后作业…
  1. 基础代码题:创建一个包含 5 个缺失值的随机数组,分别用均值和中位数填充,对比填充后的结果有何不同。
  2. 探究题:使用 PolynomialFeatures(degree=3) 处理含有 2 个特征的数组 [a, b],手动列出转换后应该包含哪些项?(答案:a, b, a², ab, b², a³, a²b, ab², b³)
  3. 辨析题:在“鸢尾花(Iris)”数据集中,如果 花瓣长度 列有缺失值,你认为该用均值还是中位数填充?为什么?(提示:鸢尾花数据非常标准,没有异常值,选均值即可)。
  4. 预习任务:我们已经把数据洗得干干净净,特征也造好了,下一步就该“调模型”了。预习 6.2 降维(PCA)——如果特征太多太胖了,怎么给它“瘦身”?
  5. 职高衔接拓展:如果你在做一个“共享单车需求量预测”项目,特征包括“温度”和“湿度”,请问生成多项式特征时,温度和湿度的交叉项(温度×湿度)在业务上可能代表什么含义?(提示:体感温度、舒适度)

📋 七、 板书设计

🛠️ 板书设计…
 1第六章 预处理(三)
 26.1.6 缺失值插补 | 6.1.7 多项式特征
 3
 4一、缺失值填坑 (Imputer)
 5   检测:df.isnull(),df.info()
 6   策略:
 7     - 均值 (mean)   -> 数据规整,无异常值
 8     - 中位数 (median) -> 有异常值(如收入)
 9     - 众数 (most_frequent) -> 类别(如性别)
10   代码模板:
11     imputer = SimpleImputer(strategy='median')
12     imputer.fit(X_train)          # 只算训练集!
13     X_train_filled = imputer.transform(X_train)
14     X_test_filled = imputer.transform(X_test)
15   ⚠️ 红线:绝对禁止先填充后分集!
16
17二、造武器 (PolynomialFeatures)
18   原理:[a, b] -> [a, b, a², ab, b²]
19   代码:PolynomialFeatures(degree=2, include_bias=False)
20   作用:让线性模型学会“乘法”和“平方”关系。
21   ⚠️ 警告:特征会变多,小心过拟合!

本课用到的单词

单词 发音 专业英语解释(中文)
Imputation /ˌɪmpjʊˈteɪʃən/ 插补。指用统计值(如均值、中位数)替换缺失值的过程。
Missing Value /ˈmɪsɪŋ ˈvæljuː/ 缺失值。数据集中未被记录或不可用的数值,通常表示为 NaN。
Median /ˈmiːdiən/ 中位数。将数据排序后位于中间的值,对极端异常值不敏感。
Polynomial /ˌpɑːliˈnoʊmiəl/ 多项式。由常数、变量和指数组成的数学表达式(如 x² + xy)。
Interaction Term /ˌɪntərˈækʃən tɜːrm/ 交互项。两个不同特征相乘生成的新特征,表示它们之间的联合影响。
Data Leakage /ˈdeɪtə ˈliːkɪdʒ/ 数据泄露。在训练模型时,测试集的信息意外地泄露给了训练集,导致评估结果不真实。
Feature Engineering /ˈfiːtʃər ˌendʒɪˈnɪrɪŋ/ 特征工程。将原始数据转化为更能代表问题本质的特征的过程。