机器学习实践项目(二)- 房价预测增强篇 - 特征工程三

独热编码详解与应用

前面的步骤中,我们构造了一些新的强特征字段,用来帮助模型的学习。接下来我们继续处理数据集中的数据。

# 2.3 有序质量列 -> 数值映射
# 这些列原来是文本(Ex/Gd/TA/Fa/Po),本质是“有序类别”
# 映射为 5~1 的分值;缺失/NA 映射为 0,表示“无/不可用”
ORD_MAP = {"Ex": 5, "Gd": 4, "TA": 3, "Fa": 2, "Po": 1, np.nan: 0, "NA": 0}
ORD_COLS = [
    "ExterQual", "ExterCond", "BsmtQual", "BsmtCond", "HeatingQC",
    "KitchenQual", "FireplaceQu", "GarageQual", "GarageCond"
]

def map_ordered_qualities(df):
    df = df.copy()
    for c in ORD_COLS:
        if c in df.columns:
            # map + fillna 双保险;astype(float) 方便后续数值处理
            df[c] = df[c].map(ORD_MAP).fillna(0).astype(float)
    return df

在第一个项目Rossmann销售预测中,我们提到过,最终参与机器学习的字段,都必须是数值类型的字段,因此,有些代表分类的字段,我们必须要把分类的枚举值,映射成数字。

如上述代码所示,ORD_COLS中存储的各字段,都是“分类”类型的字段,其取值可枚举且有限,所以统一映射为5~1,空值映射为0.

处理完有序值,我们要处理一些异常值。

# 2.4 修正 GarageYrBlt 异常(例如年份越界等)
# 若出现不合理年份:小于 1800 或 大于售出年份 YrSold,则用 YearBuilt 回填;否则置 NaN
def fix_garage_year(df):
    df = df.copy()
    if "GarageYrBlt" in df.columns:
        mask = (df["GarageYrBlt"].notna())
        if "YrSold" in df.columns:
            bad = (df["GarageYrBlt"] < 1800) | (df["GarageYrBlt"] > df["YrSold"])
        else:
            bad = (df["GarageYrBlt"] < 1800)
        if "YearBuilt" in df.columns:
            df.loc[mask & bad, "GarageYrBlt"] = df.loc[mask & bad, "YearBuilt"]
        else:
            df.loc[mask & bad, "GarageYrBlt"] = np.nan
    return df

车库的建造年份GarageYrBlt,如果早于1800年,或者晚于当前售卖年份,都是不合理的。1885年第一辆汽车才发明出来,你1800年前哪来的车库?或者你车库建造年份比当前售卖年份都晚,你玩期房呢?这些都会给模型产生噪声,降低预测准确性。所以,对数据集进行细致的分析和清洗,是提高模型学习效果的一大助力。

好,下面就进入了我认为本项目最“枯燥”确最有技术水平的部分之一,对Neighborhood列进行Out-of-Fold Target Encoding(分层目标编码)。

聊之前不得不提一下对“分类”类型字段的传统处理方式One-Hot独热编码。这个让AI来讲解最合适。

🧩 一、为什么需要独热编码?

机器学习模型(尤其是线性模型、树模型)不能直接理解文字或类别

例如这一列数据 👇

房屋IDNeighborhood(社区)
1CollgCr
2OldTown
3Edwards
4CollgCr

这里的 Neighborhood 是“类别型特征”,
模型看不懂 “CollgCr”、“OldTown” 是什么意思。
我们必须把它们变成数字。


🔢 二、错误示范:直接映射成数字会误导模型

如果你这么做:

Neighborhood编码
CollgCr1
OldTown2
Edwards3

那模型会以为:

“OldTown > CollgCr,Edwards > OldTown”

产生错误的“大小顺序”假设!
但实际上社区之间并没有大小关系。
这就是“伪数值列误导模型”的典型问题。


🧠 三、正确做法:独热编码(One-Hot Encoding)

思路:
为每一个类别新建一列,用 0/1 表示“是否属于这个类别”。

对于上面的 3 个社区,独热编码结果如下👇

房屋IDCollgCrOldTownEdwards
1100
2010
3001
4100

解释:

  • 第1行是 CollgCr → CollgCr=1,其余为 0;
  • 第2行是 OldTown → OldTown=1
  • 第3行是 Edwards → Edwards=1
  • 第4行再次是 CollgCr → 同样只激活 CollgCr 列。

“独热” 就是说在这几列中,同一行只有一个 1,其余都是 0


🧮 四、用 pandas 实现独热编码

在 Python 中非常简单:

import pandas as pd

df = pd.DataFrame({
    "Neighborhood": ["CollgCr", "OldTown", "Edwards", "CollgCr"]
})

# 独热编码
df_encoded = pd.get_dummies(df, prefix="Neighborhood", dtype=int)
print(df_encoded)

输出:

   Neighborhood_CollgCr  Neighborhood_Edwards  Neighborhood_OldTown
0                     1                     0                      0
1                     0                     0                      1
2                     0                     1                      0
3                     1                     0                      0

🧩 五、在房价预测项目中的意义

Neighborhood, HouseStyle, Exterior1st 这类字段都是类别特征。
在特征工程里,我们会用:

all_dummy_df = pd.get_dummies(all_df, dtype=int)

它会自动对所有 object 类型的列做独热编码,
生成大量新的 0/1 特征,让模型可以识别每个类别的存在与否。


💡 六、优点与缺点

优点缺点
简单易懂,几乎所有模型都能直接用当类别很多时,列数暴增(高维稀疏)
不引入虚假的“大小关系”不能捕捉类别之间的相似度(A 和 B 之间完全独立)

所以在类别很多(上百个)的场景下,我们会考虑更高级的做法,比如你前面问到的 Target Encoding(目标编码)


🚀 七、小结一句话

独热编码(One-Hot Encoding)
把每个类别拆成一组 0/1 特征,
每行数据“只点亮一个”,让模型能识别不同类别而不会被误导。


好,独热编码的介绍完了,是不是觉得很妙?是的,我也这么觉得。但是分层目标编码更妙,我们下篇文章再说。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值