决策树实战:如何用有序编码提升预测能力

【精选优质专栏推荐】


每个专栏均配有案例与图文讲解,循序渐进,适合新手与进阶学习者,欢迎订阅。

在这里插入图片描述

前言

分类变量非常关键,因为它们通常携带影响预测模型结果的重要信息。然而,它们的非数值特性在模型处理中带来了独特的挑战,因此需要特定的编码策略。

本文将讨论数据集中常见的不同类型的分类数据。我们将深入探讨有序编码,以及在实现决策树回归器(Decision Tree Regressor)时如何利用它。

通过使用 sklearn 的 OrdinalEncoder 和 Ames 房价数据集的实用 Python 示例,本指南将为你提供有效实施这些策略的技能。此外,我们还将直观展示这些编码变量如何影响决策树回归器的决策。

理解分类变量:有序(Ordinal)与无序(Nominal)

数据集中的分类特征是基础元素,在预处理过程中需要仔细处理,以确保模型预测的准确性。这些特征大体上可以分为两类:有序和无序。

有序特征在类别之间具有自然顺序或层级。例如,Ames 数据集中的 ExterQual 特征描述房屋外部材料的质量,其等级包括 “Poor”、“Fair”、“Average”、“Good” 和 “Excellent”。这些类别之间的顺序是有意义的,并且可以在预测建模中使用。相比之下,无序特征不暗示任何固有的顺序。类别是独立的,之间没有顺序关系。例如,Neighborhood 特征表示不同街区的名称,如 “CollgCr”、“Veenker”、“Crawfor”等,没有任何内在排名或层级。

分类变量的预处理至关重要,因为大多数机器学习算法要求输入数据为数值形式。将分类数据转换为数值通常通过编码实现。编码策略的选择十分关键,取决于分类变量的类型以及所使用的模型。

机器学习模型的编码策略

线性模型,例如线性回归,通常对有序和无序特征都使用独热编码(One-Hot Encoding)。该方法将每个类别转换为一个新的二元变量,确保模型将每个类别视为独立实体,而不考虑顺序关系。这一点很重要,因为线性模型假设输入为区间数据。

也就是说,线性模型会线性解释数值输入,这意味着在有序编码中为每个类别分配的整数值可能会误导模型。线性模型可能错误地假设有序编码中的每个增量整数值反映了基础量度的等步长增加,如果该假设不成立,会扭曲模型输出。

基于树的模型,包括决策树和随机森林,处理分类数据的方式不同。这些模型可以从有序特征的有序编码中受益,因为它们根据特征值进行二元拆分。有序编码中保留的顺序可以帮助模型做出更有效的拆分。树模型本质上不会评估类别之间的算术差值,而是评估在某个编码值处进行拆分是否最能将目标变量分成类别或区间。与线性模型不同,这使得它们对类别间距不敏感。

既然我们已经探讨了分类变量的类型及其对机器学习模型的影响,接下来的部分将指导你如何将这些概念付诸实践。我们将深入介绍如何在 Python 中使用 Ames 数据集实现有序编码,为你提供高效准备数据以进行模型训练的工具。

在 Python 中实现有序编码

在 Python 中实现有序编码,我们使用 sklearn.preprocessing 中的 OrdinalEncoder。这个工具在为基于树的模型准备有序特征时特别有用。它允许我们手动指定类别顺序,从而确保编码尊重数据的自然层级。我们可以使用扩展数据字典中的信息来实现这一点:

# 导入必要的库
import pandas as pd
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import FunctionTransformer, OrdinalEncoder
 
# 加载数据集
Ames = pd.read_csv('Ames.csv')
 
# 根据数据字典手动指定有序编码的类别顺序
ordinal_order = {
    'Electrical': ['Mix', 'FuseP', 'FuseF', 'FuseA', 'SBrkr'],  # 电气系统
    'LotShape': ['IR3', 'IR2', 'IR1', 'Reg'],  # 地块整体形状
    'Utilities': ['ELO', 'NoSeWa', 'NoSewr', 'AllPub'],  # 可用公用设施类型
    'LandSlope': ['Sev', 'Mod', 'Gtl'],  # 地块坡度
    'ExterQual': ['Po', 'Fa', 'TA', 'Gd', 'Ex'],  # 房屋外部材料质量
    'ExterCond': ['Po', 'Fa', 'TA', 'Gd', 'Ex'],  # 外部材料当前状况
    'BsmtQual': ['None', 'Po', 'Fa', 'TA', 'Gd', 'Ex'],  # 地下室高度
    'BsmtCond': ['None', 'Po', 'Fa', 'TA', 'Gd', 'Ex'],  # 地下室整体状况
    'BsmtExposure': ['None', 'No', 'Mn', 'Av', 'Gd'],  # 地下室走出式或花园级墙面
    'BsmtFinType1': ['None', 'Unf', 'LwQ', 'Rec', 'BLQ', 'ALQ', 'GLQ'],  # 地下室装修区域质量
    'BsmtFinType2': ['None', 'Unf', 'LwQ', 'Rec', 'BLQ', 'ALQ', 'GLQ'],  # 第二地下室装修区域质量
    'HeatingQC': ['Po', 'Fa', 'TA', 'Gd', 'Ex'],  # 供暖质量与状况
    'KitchenQual': ['Po', 'Fa', 'TA', 'Gd', 'Ex'],  # 厨房质量
    'Functional': ['Sal', 'Sev', 'Maj2', 'Maj1', 'Mod', 'Min2', 'Min1', 'Typ'],  # 房屋功能性
    'FireplaceQu': ['None', 'Po', 'Fa', 'TA', 'Gd', 'Ex'],  # 壁炉质量
    'GarageFinish': ['None', 'Unf', 'RFn', 'Fin'],  # 车库内部装修
    'GarageQual': ['None', 'Po', 'Fa', 'TA', 'Gd', 'Ex'],  # 车库质量
    'GarageCond': ['None', 'Po', 'Fa', 'TA', 'Gd', 'Ex'],  # 车库状况
    'PavedDrive': ['N', 'P', 'Y'],  # 铺砌车道
    'PoolQC': ['None', 'Fa', 'TA', 'Gd', 'Ex'],  # 游泳池质量
    'Fence': ['None', 'MnWw', 'GdWo', 'MnPrv', 'GdPrv']  # 围栏质量
}
 
# 从字典中提取所有有序特征的列表
ordinal_features = list(ordinal_order.keys())
 
# 排除 Electrical 的有序特征列表
ordinal_except_electrical = [feature for feature in ordinal_features if feature != 'Electrical']
 
# 对 'Electrical' 特征使用众数进行缺失值填补
electrical_imputer = Pipeline(steps=[
    ('impute_electrical', SimpleImputer(strategy='most_frequent'))
])
 
# 辅助函数,用 'None' 填充其他有序特征
def fill_none(X):
    return X.fillna("None")
 
# 有序特征流水线:用 'None' 填充缺失值
ordinal_imputer = Pipeline(steps=[
    ('fill_none', FunctionTransformer(fill_none, validate=False))
])
 
# 填充缺失值的预处理器
preprocessor_fill = ColumnTransformer(transformers=[
    ('electrical', electrical_imputer, ['Electrical']),
    ('cat', ordinal_imputer, ordinal_except_electrical)
])
 
# 应用预处理器填充缺失值
Ames_ordinal = preprocessor_fill.fit_transform(Ames[ordinal_features])
 
# 转回 DataFrame 以应用 OrdinalEncoder
Ames_ordinal = pd.DataFrame(Ames_ordinal, columns=['Electrical'] + ordinal_except_electrical)
 
# 应用有序编码
categories = [ordinal_order[feature] for feature in ordinal_features]
ordinal_encoder = OrdinalEncoder(categories=categories)
Ames_ordinal_encoded = ordinal_encoder.fit_transform(Ames_ordinal)
Ames_ordinal_encoded = pd.DataFrame(Ames_ordinal_encoded, columns=['Electrical'] + ordinal_except_electrical)

上面的代码块通过先填充缺失值再应用适当的编码策略,有效地处理了分类变量的预处理。通过在编码前查看数据集,我们可以确认预处理步骤已正确应用:

# 编码前 Ames 数据集的有序特征
print(Ames_ordinal)
     Electrical LotShape Utilities LandSlope  ... GarageCond PavedDrive PoolQC Fence
0         SBrkr      Reg    AllPub       Gtl  ...         TA          Y   None  None
1         SBrkr      Reg    AllPub       Gtl  ...         TA          Y   None  None
2         SBrkr      Reg    AllPub       Gtl  ...         Po          N   None  None
3         SBrkr      Reg    AllPub       Gtl  ...         TA          N   None  None
4         SBrkr      Reg    AllPub       Gtl  ...         TA          Y   None  None
...         ...      ...       ...       ...  ...        ...        ...    ...   ...
2574      FuseF      Reg    AllPub       Gtl  ...         Po          P   None  None
2575      FuseA      IR1    AllPub       Gtl  ...         TA          Y   None  None
2576      FuseA      Reg    AllPub       Gtl  ...         TA          Y   None  None
2577      SBrkr      Reg    AllPub       Gtl  ...         TA          Y   None  None
2578      SBrkr      IR1    AllPub       Gtl  ...         TA          Y   None  None
 
[2579 rows x 21 columns]

上面的输出显示了 Ames 数据集中在任何有序编码之前的有序特征。下面,我们演示提供给 OrdinalEncoder 的具体信息。请注意,我们并未提供特征列表,只提供了每个特征在数据集中出现顺序的排名。

我们输入到 OrdinalEncoder 的信息,它会自动分配 0、1、2、3 等数值。

print(categories)
[['Mix', 'FuseP', 'FuseF', 'FuseA', 'SBrkr'], 
 ['IR3', 'IR2', 'IR1', 'Reg'], 
 ['ELO', 'NoSeWa', 'NoSewr', 'AllPub'], 
 ['Sev', 'Mod', 'Gtl'], 
 ['Po', 'Fa', 'TA', 'Gd', 'Ex'], 
 ['Po', 'Fa', 'TA', 'Gd', 'Ex'], 
 ['None', 'Po', 'Fa', 'TA', 'Gd', 'Ex'], 
 ['None', 'Po', 'Fa', 'TA', 'Gd', 'Ex'], 
 ['None', 'No', 'Mn', 'Av', 'Gd'], 
 ['None', 'Unf', 'LwQ', 'Rec', 'BLQ', 'ALQ', 'GLQ'], 
 ['None', 'Unf', 'LwQ', 'Rec', 'BLQ', 'ALQ', 'GLQ'], 
 ['Po', 'Fa', 'TA', 'Gd', 'Ex'], 
 ['Po', 'Fa', 'TA', 'Gd', 'Ex'], 
 ['Sal', 'Sev', 'Maj2', 'Maj1', 'Mod', 'Min2', 'Min1', 'Typ'], 
 ['None', 'Po', 'Fa', 'TA', 'Gd', 'Ex'], 
 ['None', 'Unf', 'RFn', 'Fin'], 
 ['None', 'Po', 'Fa', 'TA', 'Gd', 'Ex'], 
 ['None', 'Po', 'Fa', 'TA', 'Gd', 'Ex'], 
 ['N', 'P', 'Y'], 
 ['None', 'MnWw', 'GdWo', 'MnPrv', 'GdPrv']]

这为有效应用有序编码奠定了基础,其中类别的自然顺序对后续模型训练至关重要。特征中的每个类别将被转换为数值,反映其指定的排名或重要性,而不假定类别之间有等距间隔。

# 编码后的 Ames 数据集有序特征
print(Ames_ordinal_encoded)

下方展示了转换后的数据集。强烈建议快速检查与原始数据集是否一致,以确保结果与我们从数据字典获得的信息相符。

      Electrical  LotShape  Utilities  ...  PavedDrive  PoolQC  Fence
0            4.0       3.0        3.0  ...         2.0     0.0    0.0
1            4.0       3.0        3.0  ...         2.0     0.0    0.0
2            4.0       3.0        3.0  ...         0.0     0.0    0.0
3            4.0       3.0        3.0  ...         0.0     0.0    0.0
4            4.0       3.0        3.0  ...         2.0     0.0    0.0
...          ...       ...        ...  ...         ...     ...    ...
2574         2.0       3.0        3.0  ...         1.0     0.0    0.0
2575         3.0       2.0        3.0  ...         2.0     0.0    0.0
2576         3.0       3.0        3.0  ...         2.0     0.0    0.0
2577         4.0       3.0        3.0  ...         2.0     0.0    0.0
2578         4.0       2.0        3.0  ...         2.0     0.0    0.0

[2579 rows x 21 columns]

在本段关于实现有序编码的内容中,我们通过精确地将每个有序特征映射到其内在层级值,为后续分析打下了坚实基础。这使我们的预测模型能够更好地理解并利用数据中固有的结构关系。对编码细节的认真处理,为更深入和精确的建模奠定了基础。

可视化决策树:从有序编码数据中获得洞察

在本文的最后部分,我们将深入探讨决策树回归器如何解释和利用这些经过精心编码的数据。我们将直观地探索树的决策过程,突出显示有序特征如何影响模型内部的路径和决策。

这种可视化不仅确认了正确数据准备的重要性,还以直观的方式揭示了模型的推理过程。有了经过仔细预处理和编码的分类变量,我们的数据集已为下一关键步骤做好准备:训练决策树回归器。

# 基于上述代码块
# 导入必要的库
from sklearn.tree import DecisionTreeRegressor
from sklearn.model_selection import train_test_split
import dtreeviz
 
# 加载并拆分数据
X_ordinal = Ames_ordinal_encoded # 仅使用有序特征拟合模型
y = Ames['SalePrice']
X_train, X_test, y_train, y_test = train_test_split(X_ordinal, y, test_size=0.2, random_state=42)
 
# 初始化并拟合决策树
tree_model = DecisionTreeRegressor(max_depth=3)
tree_model.fit(X_train.values, y_train)
 
# 使用 dtreeviz 可视化决策树
viz = dtreeviz.model(tree_model, X_train, y_train,
               target_name='SalePrice', feature_names=X_train.columns.tolist())
 
# 在 Jupyter Notebook 中,可直接使用以下方式查看可视化:
# viz.view()  # 渲染并显示 SVG 可视化图
 
# 在 PyCharm 中,可渲染并显示 SVG 图像:
v = viz.view()     # 渲染为内部对象的 SVG
v.show()           # 弹出窗口显示

通过可视化决策树,我们提供了模型如何处理特征以得出预测结果的图形化表示。

在这里插入图片描述

在这棵树中用于划分的特征包括 ExterQual、FireplaceQu、BsmtQual、GarageQual 和 KitchenQual。这些特征是根据它们在用于划分数据时能够降低均方误差(MSE)的能力被选择的。这些划分的等级或阈值(例如 ExterQual <= 2.5)是在训练过程中确定的,以优化将数据点划分为更同质化的组。

该可视化不仅验证了我们编码策略的有效性,还展示了决策树在预测建模中所带来的战略深度。

总结

本文介绍了有序(Ordinal)与无序(Nominal)分类变量的区别。通过使用 Python 以及 sklearn 的 OrdinalEncoder 实现有序编码。最后,通过对该编码数据可视化决策树,提供了直观的洞察,使读者更清晰地理解模型是如何基于所提供的特征进行预测的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秋说

感谢打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值