🧩 模块2:"翻译现实世界:编码分类变量"

目标:

学会将文本、标签和类别转换为模型能理解的数字——而不引入偏差或失真。


2.1 为什么要编码?

机器学习算法(回归、SVM、神经网络)只理解数字。 它们无法直接处理"Visa"、"Mastercard"或"Spain"。

但是…小心!这不仅仅是分配任意数字。您如何编码会影响模型性能和解释。


2.2 标签编码

为每个类别分配一个唯一的整数。

from sklearn.preprocessing import LabelEncoder

le = LabelEncoder()
df['卡片类型_编码'] = le.fit_transform(df['卡片类型'])

# 示例:["Visa", "Mastercard", "Amex"] → [0, 1, 2]

⚠️ 严重问题: 引入人工顺序。模型可能解释为"2" > "1" > "0",好像"Amex"比"Visa"更好。如果不存在实际顺序,这是不正确的。

使用时机: 仅用于有序变量(例如,"低"、"中"、"高")或决策树(不假设线性顺序)。


2.3 独热编码

为每个类别创建一个二进制列(0/1)。

from sklearn.preprocessing import OneHotEncoder
import pandas as pd

ohe = OneHotEncoder(sparse_output=False, drop='first')  # drop='first'避免多重共线性
categories_encoded = ohe.fit_transform(df[['卡片类型']])

# 转换为带名称的DataFrame
df_ohe = pd.DataFrame(categories_encoded, columns=ohe.get_feature_names_out(['卡片类型']))
df = pd.concat([df.reset_index(drop=True), df_ohe], axis=1)

优点:

  • 不引入虚假顺序。
  • 适合线性模型、SVM、神经网络。

⚠️ 缺点:

  • 大大增加维度(50个国家→50列)。
  • 可能导致"维度诅咒"。

2.4 目标编码

用该类别的目标变量均值替换每个类别。

# 示例:卡片类型→该卡片类型的"是否欺诈"均值
target_mean = df.groupby('卡片类型')['是否欺诈'].mean()
df['卡片类型_目标编码'] = df['卡片类型'].map(target_mean)

优点:

  • 直接捕获预测信息。
  • 不增加维度。

⚠️ 风险:

  • 如果在分割训练/测试之前计算,会导致数据泄露。
  • 如果类别样本少,会导致过拟合。

解决方案: 使用交叉验证或添加噪声。


📊 可视化:比较编码

fig, ax = plt.subplots(1, 3, figsize=(15, 5))

# 原始
sns.countplot(data=df, x='卡片类型', ax=ax[0])
ax[0].set_title("原始")

# 标签编码
sns.histplot(df['卡片类型_编码'], bins=3, ax=ax[1])
ax[1].set_title("标签编码")

# 独热编码(显示一列)
sns.histplot(df['卡片类型_Mastercard'], bins=2, ax=ax[2])
ax[2].set_title("独热编码(例如,Mastercard)")

plt.tight_layout()
plt.show()

📝 练习2.1:智能编码

数据集: 欺诈_清理.csv(来自上一模块)

任务:

  1. 风险等级列应用标签编码(假设它是有序的:"低"、"中"、"高")。
  2. 卡片类型国家应用独热编码。使用drop='first'
  3. 城市应用目标编码(仅使用训练集计算均值——避免数据泄露!)。
  4. 比较编码前后的DataFrame形状。创建了多少新列?
  5. 删除原始文本列(仅保留编码列)。

💡 附加说明:

  • 总是在分割训练/测试之后编码以避免数据泄露。
  • 独热编码是大多数模型的标准。
  • 目标编码强大但危险。 与交叉验证谨慎使用。
  • Scikit-learn有OrdinalEncoder用于有序变量(比LabelEncoder更适合多列)。


Course Info

Course: AI-course1

Language: ZH

Lesson: Module2