6-2分类编码、二值化与非线性转换

🎯 教学目标与重难点…

【三维目标】

  • 📚 知识目标
    1. 理解为什么机器学习模型不能直接处理“文本”或“类别”数据。
    2. 掌握 LabelEncoder(标签编码)和 OneHotEncoder / pd.get_dummies(独热编码)的区别与使用。
    3. 了解二值化(Binarizer)的作用——把数值变为“有/无”开关。
    4. 了解非线性转换(np.log1p)的作用——压缩极端大数(如房价、收入)的分布。
  • ⚙️ 能力目标
    1. 能准确判断一个特征是“有序类别”还是“无序类别”,并选择正确的编码方式。
    2. 能使用 Pandas 的 get_dummies 一键完成独热编码。
  • 💡 素养目标
    1. 建立“机器学习很笨,只认数字,我们要帮它翻译”的工程思维。
    2. 避免“把颜色编码成 1,2,3 导致模型认为红色大于蓝色”的经典错误。

【重点与难点】

  • 🟢 教学重点pd.get_dummies 独热编码的原理与代码实现(应对性别、颜色、地区等文本特征)。
  • 🟡 教学难点为什么要避免把“颜色”直接映射为 1,2,3 ?(即:无序分类变量绝对不能有大小顺序)。

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

复习上节课:我们学会了把“体重(kg)”和“时长(分钟)”缩放到同一量级。

新问题(灵魂拷问): 现在我们有一张“学生信息表”:

姓名 性别 班级 是否住校
张三 计应1班
李四 计应2班

问题:如果我们想把“性别”和“班级”作为特征输入给逻辑回归或 KNN 模型,直接把这些汉字扔进去,代码会直接报错!

思考:机器学习模型是个“数学计算器”,它只认识数字(整数和小数),完全不认识“男”“女”“计应1班”。

引出解决方案:我们必须把这些文本标签翻译成数字。这就是 分类特征编码

  • 👨‍🏫 教师活动:在 PPT 上展示学生信息表,提问:“如果让你给计算机设计规则,你怎么把‘男’和‘女’变成数字?”
  • 🧑‍🎓 学生活动:学生本能回答“男=1,女=2”或“男=0,女=1”。老师先肯定这是最直接的办法,然后话锋一转——“但是这个简单粗暴的办法藏着大坑!” 吊起胃口。

从学生最熟悉的“班级花名册”切入,让他们意识到机器“不识字”的困境。通过肯定学生“男=1女=0”的直觉,再引出隐患,制造认知冲突。


📖 二、 解决问题过程(一):分类特征编码(核心难点攻克)

1. 场景模拟:给颜色编个号 假设特征“颜色”有 3 个取值:红、绿、蓝。 如果按照直觉编码:红=1,绿=2,蓝=3。 计算距离时:红(1)与绿(2)的距离是 1,红(1)与蓝(3)的距离是 2。
推导出的荒谬结论:模型会认为 “红色跟绿色更像,红色跟蓝色更不像” ! 但在现实世界中,颜色只是“类别”,没有数学上的远近之分。红色并不比蓝色更接近绿色!

2. 解决方案 ①:独热编码(One-Hot Encoding) —— 解决“无序类别”的最佳方案

  • 原理:一个类别有 N 种取值,就创建 N 个新的 0/1 特征列。
  • 例子(红绿蓝)
    • 红色 → [1, 0, 0]
    • 绿色 → [0, 1, 0]
    • 蓝色 → [0, 0, 1]
  • 结论:任意两个颜色之间的距离都是 \( \sqrt{1^2+1^2} = \sqrt{2} \),绝对平等,没有谁比谁更近!

3. 代码实操(Pandas 一键生成)

 1import pandas as pd
 2
 3# 原始数据(颜色)
 4df = pd.DataFrame({'颜色': ['红', '绿', '蓝', '红']})
 5
 6# 一键独热编码
 7df_encoded = pd.get_dummies(df, columns=['颜色'])
 8
 9print(df_encoded)
10# 输出结果:
11#    颜色_红  颜色_绿  颜色_蓝
12# 0     1      0      0
13# 1     0      1      0
14# 2     0      0      1
15# 3     1      0      0

4. 解决方案 ②:标签编码(Label Encoding) —— 只用于“有序类别”

  • 什么时候用?当类别本身有明确的高低、等级顺序时。比如:学历 = 小学(0)、初中(1)、高中(2)、大学(3)。
  • 代码from sklearn.preprocessing import LabelEncoder。但职高阶段强烈推荐优先使用独热编码,除非特征取值非常多(超过几十个)且有序。
  • 👨‍🏫 教师活动
    1. 在黑板上画出红绿蓝三角形,演示如果编码为 1,2,3 会变成一条直线(不公平),而独热编码变成三维空间三个顶点(平等)。
    2. 敲代码演示 pd.get_dummies,强调这行代码在职场中每天被使用成千上万次。
  • 🧑‍🎓 学生活动
    1. 小组讨论:“手机品牌(华为、小米、苹果)”和“衣服尺码(S、M、L)”分别应该用哪种编码?(答案:品牌用独热,尺码用标签)。
    2. 学生跟着敲代码,观察列名是如何从“颜色”变成“颜色_红”的。

通过“红绿蓝”的数学证明,彻底粉碎“男=0女=1就行了”的认知误区。让职高生记住一句话:“没有顺序的类别,必须独热!” 这是面试和工作中极易踩的坑。


💻 三、 解决问题过程(二):二值化与非线性转换(辅助技能)

1. 二值化(Binarizer)—— 把数字变成“是与否”开关

场景:预测学生是否玩游戏上瘾。特征“每日在线时长(分钟)”并不需要精确的 125 分钟,我们只关心“是否超过 2 小时(120 分钟)”。

  • 操作:设定阈值,大于阈值的变为 1,小于等于的变为 0。
  • 代码
1from sklearn.preprocessing import Binarizer
2
3data = [[50], [130], [200], [30]]
4binarizer = Binarizer(threshold=120)  # 阈值设为120分钟
5result = binarizer.fit_transform(data)
6print(result)  # 输出:[[0], [1], [1], [0]]   (超过2小时为1)

2. 非线性转换(对数变换)—— 收拾“极端大数”刺头

场景:分析居民收入。马云年收入 1 个亿(100000000),普通人年收入 5 万(50000)。

  • 问题:如果直接做归一化,马云那个 1 亿会把所有普通人的收入压成接近 0,模型完全看不到普通人的差异。
  • 对策:取对数 \( \log(数据) \),把巨大的差距压缩成较小的差距。
  • 代码
1import numpy as np
2incomes = [50000, 60000, 100000000]  # 普通人和马云
3log_incomes = np.log1p(incomes)  # log1p 是 log(1+x),防止出现 0 导致负无穷
4print(log_incomes)
5# 输出:普通人的值从 5万 变成了 10.8,马云从 1亿 变成了 18.4,差距被大大缩小!
  • 👨‍🏫 教师活动
    1. 对于二值化,提问:“判断一个人是否成年(18岁),属于什么操作?”(答案:二值化)。
    2. 对于非线性转换,用“身高”举例:姚明太高,把其他人都显得矮了,取对数就像把姚明的腿锯掉一截拉到同一张图里对比(纯比喻,帮助学生理解压缩)。
  • 🧑‍🎓 学生活动
    1. 学生动手运行 Binarizer 代码,修改阈值观察变化。
    2. 学生打印 log1p 前后的数组形状,感受“大数变小”的神奇。

这两块属于“锦上添花”的预处理技巧。不求全部记住,但要求学生在脑子里有个印象:“如果遇到 0/1 判断题用二值化,如果遇到有几个数字特别大,用 log 压一压。”


✍️ 四、 解决问题过程(三):课堂实战——清洗“二手汽车”数据

📝 任务一:综合预处理实战(小组接力赛)…

背景与题目: 你是一名二手车评估师,拿到了下面 3 辆车的简易数据,需要喂给机器学习模型预测价格。请完成预处理:

品牌 颜色 车龄(年) 是否事故车
大众 3
宝马 10
大众 1

请按步骤操作

  1. 编码:对“品牌”和“颜色”进行独热编码(使用 pd.get_dummies)。
  2. 二值化:对“是否事故车”(是/否)进行标签编码(是=1,否=0)。提示:可以用 replace 或者直接单独处理这一列。
  3. 思考:如果对“车龄”做标准化好,还是做归一化好?(提示:车龄 1~10 年,没有极端异常值,两者皆可,但如果后续用 KNN,必须归一化)。
🔍 查看参考代码与解析…
 1import pandas as pd
 2from sklearn.preprocessing import Binarizer # 仅用于演示,本任务不必须
 3
 4# 1. 原始数据
 5df = pd.DataFrame({
 6    '品牌': ['大众', '宝马', '大众'],
 7    '颜色': ['白', '红', '黑'],
 8    '车龄': [3, 10, 1],
 9    '是否事故车': ['否', '是', '否']
10})
11
12# 2. 独热编码(品牌和颜色)
13df_encoded = pd.get_dummies(df, columns=['品牌', '颜色'])
14print("独热编码结果:")
15print(df_encoded)
16
17# 3. 二值化(是否事故车)
18df_encoded['是否事故车'] = df_encoded['是否事故车'].map({'是': 1, '否': 0})
19print("\n最终处理结果:")
20print(df_encoded)
21
22# 输出结果:
23#    车龄  是否事故车  品牌_大众  品牌_宝马  颜色_白  颜色_红  颜色_黑
24# 0   3       0         1         0       1       0       0
25# 1  10       1         0         1       0       1       0
26# 2   1       0         1         0       0       0       1

解析:大众品牌在“品牌_大众”列变成 1,颜色“白”在“颜色_白”列变成 1。现在这 3 条数据已经全是数字,可以直接建模了!


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

flowchart LR
    root["🧹 预处理(二) 把文本/奇葩数变数字"]

    subgraph C1 ["🏷️ 分类编码(核心)"]
        direction TB
        A1["无序类别(颜色/品牌)"]
        A2["👉 独热编码 (get_dummies)"]
        A3["有序类别(学历/尺码)"]
        A4["👉 标签编码 (LabelEncoder)"]
    end

    subgraph C2 ["🔌 二值化"]
        direction TB
        B1["数值 -> 0/1 开关"]
        B2["阈值判断(超时/成年)"]
    end

    subgraph C3 ["📉 非线性转换"]
        direction TB
        C1["压缩极端大数"]
        C2["log1p 让数据分布更集中"]
    end

    root --> C1
    root --> C2
    root --> C3

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

✏️ 随堂检测与互动练习

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

一、 单选题

  1. 以下哪个特征最适合使用独热编码(One-Hot Encoding)?
  • A. 身高(cm):[160, 170, 180]
  • B. 城市:[‘北京’, ‘上海’, ‘广州’]
  • C. 考试成绩等级:[‘A’, ‘B’, ‘C’]
  • D. 温度(℃):[25, 26, 27]
【答案】

【解析】B。城市之间没有大小顺序之分,只能用独热。C选项的成绩等级有优劣之分(A>B>C),适合用标签编码 0,1,2。

  1. 如果对“颜色”直接用红=1,绿=2,蓝=3进行编码,会带来什么问题?
  • A. 计算时会报错,因为数字超出范围
  • B. 模型会误认为“红色”与“绿色”的距离比“红色”与“蓝色”的距离小
  • C. 数据量会爆炸,占用大量内存
  • D. 没有任何问题,这就是标准做法
【答案】

【解析】B。这是本节课最核心的坑!模型会把 1、2、3 当成数轴上的点,从而强行赋予颜色“大小”关系。

二、 代码填空题

题目:现有 DataFrame df 包含一列 ['猫', '狗', '猫', '兔子'],请使用 Pandas 将其转换为独热编码格式,并存入变量 df_dummies

1import pandas as pd
2df = pd.DataFrame({'动物': ['猫', '狗', '猫', '兔子']})
3
4# 请在横线处填写代码
5df_dummies = _________________________________
6print(df_dummies)
【答案】

pd.get_dummies(df, columns=['动物'])


📮 六、 课后作业与拓展

📮 课后作业…
  1. 基础代码题:给定一个包含 ['苹果', '香蕉', '苹果', '橘子'] 的列表,使用 pd.get_dummies 转换,并观察列名的命名规则。
  2. 辨析题:判断以下特征应采用“独热编码”还是“标签编码”:
    • (a) 手机价格区间(低端、中端、高端)
    • (b) 国家名称(中国、美国、日本)
    • (c) 一周中的星期几(周一、周二…周日)(提示:周一和周日有关系吗?思考是不是循环的)
  3. 进阶挑战:去网上找一个关于“泰坦尼克号”的数据集(CSV格式),用 Pandas 读取,对其中的 Sex(性别)和 Embarked(登船港口)列进行独热编码。
  4. 预习任务:如果数据里面有缺失值(比如年龄没填),我们该怎么办?预习 6.1.6 缺失值插补。
  5. 职高衔接拓展:假设你在做电商数据分析,特征“好评度”分“好评、中评、差评”,这属于有序还是无序?编码为 2,1,0 合理吗?

📋 七、 板书设计

🛠️ 板书设计…
 1第六章 预处理(二)
 26.1.5 分类编码 | 6.1.4 二值化 | 6.1.2 非线性转换
 3
 4一、分类特征编码(核心!)
 5   1. 无序类别(性别/颜色/品牌)
 6      -> 必须用独热编码 (One-Hot)
 7      -> 代码: pd.get_dummies(df, columns=['列名'])
 8      -> 原理:创建 N 列 0/1,保证类别间距离相等!
 9   2. 有序类别(学历/等级)
10      -> 可用标签编码 (Label) : 0, 1, 2, 3
11
12二、二值化(Binarizer)
13   连续数 -> 0/1 开关
14   (超过阈值=1,否则=0)
15
16三、非线性转换(log1p)
17   压缩极端大数(如收入、面积)
18   np.log1p(数据)
19
20⚠️ 血泪教训:千万别把“颜色/城市”编码成 1,2,3!

本课用到的单词

单词 发音 专业英语解释(中文)
Encoding /ɪnˈkoʊdɪŋ/ 编码。将文本或分类数据转换为数值型数据的过程。
One-Hot Encoding /wʌn hoʊt ɪnˈkoʊdɪŋ/ 独热编码。将无序分类变量扩展为多个二进制特征列的方法。
Label Encoding /ˈleɪbəl ɪnˈkoʊdɪŋ/ 标签编码。将有序分类变量直接映射为整数 0, 1, 2… 的方法。
Binarization /ˌbaɪnəraɪˈzeɪʃən/ 二值化。根据阈值将数值特征转换为 0 或 1 的过程。
Log Transformation /lɔːg ˌtrænsfərˈmeɪʃən/ 对数变换。使用对数函数压缩数据尺度,常用于处理长尾分布的极端值。
Dummy Variable /ˈdʌmi ˈveriəbl/ 虚拟变量。统计学中对独热编码后生成的 0/1 列的称呼。
Threshold /ˈθrɛʃhoʊld/ 阈值(或"临界值")。在机器学习中,阈值用于决定模型输出属于哪个类别(如逻辑回归中概率 ≥ 0.5 判定为正类),或控制算法停止迭代的条件(如梯度下降的误差容限)。