目录

scikit-learn中的树算法 不能直接使用分类变量

scikit-learn中的算法都不直接支持分类变量

不只是scikit-learn中的树算法不能直接使用分类变量,scikit-learn中的算法都不直接支持分类变量。在使用这些算法之前,需要将分类变量转换为独热编码(one-hot)或整数类型。

下面这张截图来自 scikit-learn 的社区,解释了为什么 scikit-learn 不能直接使用分类变量,而需要在建模前将其预处理为数值型特征。截图指出,这是因为 scikit-learn 使用 NumPy 数组或 SciPy 的稀疏矩阵来表示数据集,这两种数据结构都无法直接表示类别变量。

图片来源

举例来说,在天气这个类别变量特征中,是不能用”Rain”、”Sunny”、”Cloudy”这样的字符串形式的,而是应该使用[1, 0, 0]、[0, 1, 0]、[0, 1, 0] 这样的独热编码来分别表示 “Rain”、 “Sunny”、 “Cloudy”。

除one-hot编码之外,也可以转成整数的编码,如使用0、1、2这样的整数表示”Rain”、”Sunny”、”Cloudy”。但这种表示方式会错误地赋予分类值大小关系,而这并非分类变量本身所具有的特性,所以应避免将类别变量转换成整数形式,而应使用独热编码形式。

可以看出,scikit-learn 只能处理数值型特征,如整数或浮点数。它无法直接处理文本形式的分类变量。因此,需要将分类变量转换为独热编码形式,即包含 {0, 1} 两个数值。这里的 0 和 1 虽然是数值型变量,但它们实际上表示某个特征的存在与否。本质上,这是使用整数 0 和 1 来表示逻辑假和逻辑真。

分类变量的转换例程

下面看一个例子,这是一个使用OneHotEncoder将分类变量转换为独热编码形式的示例代码:

import pandas as pd
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, classification_report
from sklearn.preprocessing import OneHotEncoder
from sklearn.compose import ColumnTransformer

# 创建一个包含离散特征的示例数据集
data = {
    '颜色': ['红', '蓝', '绿', '红', '蓝', '绿', '红', '蓝'],
    '大小': ['大', '中', '小', '大', '中', '小', '中', '小'],
    '形状': ['圆', '方', '三角', '圆', '方', '三角', '方', '圆'],
    '类别': ['A', 'B', 'A', 'A', 'B', 'B', 'A', 'B']
}

# 创建DataFrame
df = pd.DataFrame(data)

# 分离特征和目标变量
X = df.drop('类别', axis=1)
y = df['类别']

# 创建ColumnTransformer对象来应用One-Hot编码
ct = ColumnTransformer([('encoder', OneHotEncoder(sparse=False), ['颜色', '大小', '形状'])], remainder='passthrough')

# 对特征进行One-Hot编码
X_encoded = ct.fit_transform(X)

# 获取编码后的特征名称
feature_names = ct.named_transformers_['encoder'].get_feature_names_out(['颜色', '大小', '形状'])

# 创建包含编码后特征的DataFrame
X_encoded_df = pd.DataFrame(X_encoded, columns=feature_names)

# 打印 X_encoded_df 的内容
print("X_encoded_df 的内容:")
print(X_encoded_df)

# 可以添加以下行来限制输出的行数,如果数据集很大的话
# print(X_encoded_df.head(10))  # 只打印前10行

# 打印 X_encoded_df 的基本信息
print("\nX_encoded_df 的基本信息:")
print(X_encoded_df.info())

# 打印 X_encoded_df 的统计摘要
print("\nX_encoded_df 的统计摘要:")
print(X_encoded_df.describe())

# 分割数据集为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X_encoded_df, y, test_size=0.2, random_state=42)

# 创建并训练CART决策树
cart = DecisionTreeClassifier(random_state=42)
cart.fit(X_train, y_train)

# 在测试集上进行预测
y_pred = cart.predict(X_test)

# 计算准确率
accuracy = accuracy_score(y_test, y_pred)
print(f"模型准确率: {accuracy:.2f}")

# 打印分类报告
print("\n分类报告:")
print(classification_report(y_test, y_pred))

# 打印特征重要性
print("\n特征重要性:")
for feature, importance in zip(X_encoded_df.columns, cart.feature_importances_):
    print(f"{feature}: {importance:.4f}")

# 可视化决策树
from sklearn.tree import plot_tree
import matplotlib.pyplot as plt

plt.figure(figsize=(20,10))
plot_tree(cart, 
          feature_names=list(X_encoded_df.columns),
          class_names=list(cart.classes_),
          filled=True, 
          rounded=True)
plt.show()

该代码展示了如何使用决策树模型(CART)对离散类别变量进行处理,并且通过 One-Hot Encoding 的方式将类别变量转换为数值型特征。下面我将逐步解释代码的各个部分,并详细说明如何将类别变量转变成 One-Hot 变量。

1. 创建示例数据集

data = {
    '颜色': ['红', '蓝', '绿', '红', '蓝', '绿', '红', '蓝'],
    '大小': ['大', '中', '小', '大', '中', '小', '中', '小'],
    '形状': ['圆', '方', '三角', '圆', '方', '三角', '方', '圆'],
    '类别': ['A', 'B', 'A', 'A', 'B', 'B', 'A', 'B']
}
df = pd.DataFrame(data)

这段代码创建了一个包含 8 条记录的小型数据集。数据集中的特征变量是 颜色大小形状,目标变量是 类别。每个特征变量都是离散类别变量,并且目标变量“类别”包含 AB 两个类别。

2. 特征与目标变量分离

X = df.drop('类别', axis=1)  # 特征
y = df['类别']               # 目标变量

将特征和目标变量分离,X 中包含了所有特征(颜色、大小、形状),而 y 包含了目标变量“类别”。

3. 创建 ColumnTransformer 对象并进行 One-Hot Encoding

ct = ColumnTransformer([('encoder', OneHotEncoder(sparse=False), ['颜色', '大小', '形状'])], remainder='passthrough')

这段代码创建了一个 ColumnTransformer 对象,该对象用于将 One-Hot Encoding 应用于指定的列(颜色大小形状)。ColumnTransformersklearn 中用于将不同的预处理操作应用于不同列的工具。它的主要参数如下:

  • [(‘encoder’, OneHotEncoder(sparse=False), [‘颜色’, ‘大小’, ‘形状’])]
    • encoder 是一个别名,用于标识该 ColumnTransformer 中的 OneHotEncoder 编码器。
    • OneHotEncoder(sparse=False) 指定使用 One-Hot 编码器来将离散类别特征转换为 One-Hot 格式,并且 sparse=False 表示返回的编码结果是一个密集矩阵(Dense Matrix),而不是稀疏矩阵(Sparse Matrix)。
    • ['颜色', '大小', '形状'] 指定需要进行 One-Hot Encoding 的列。
  • remainder='passthrough'
    • 表示其他未被列出用于 One-Hot Encoding 的列(在本例中不存在)将不作任何处理,直接保留在输出中。

4. 将特征进行 One-Hot Encoding

X_encoded = ct.fit_transform(X)

这段代码将 One-Hot Encoding 应用于特征 Xfit_transform 方法会:

  1. 计算每个特征中所有类别值的 One-Hot 编码映射关系。
  2. 将所有类别变量转换为 One-Hot 形式的数值矩阵。

5. 获取 One-Hot Encoding 后的特征名称

feature_names = ct.named_transformers_['encoder'].get_feature_names_out(['颜色', '大小', '形状'])

使用 ct.named_transformers_['encoder'].get_feature_names_out() 来获取编码后的特征名称。每个特征会根据其原始特征名称和类别值生成相应的 One-Hot 特征名。例如,颜色中的红、蓝、绿会生成特征名:颜色_红, 颜色_蓝, 颜色_绿

6. 创建包含 One-Hot 编码后的 DataFrame

X_encoded_df = pd.DataFrame(X_encoded, columns=feature_names)

One-Hot 编码后的矩阵转换为 DataFrame 格式,并赋予对应的列名。打印输出时可以看到如下形式的表格:

颜色_红 颜色_蓝 颜色_绿 大小_大 大小_中 大小_小 形状_圆 形状_方 形状_三角
1.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0
0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0
0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0

7. 数据集划分、模型训练与测试

X_train, X_test, y_train, y_test = train_test_split(X_encoded_df, y, test_size=0.2, random_state=42)
cart = DecisionTreeClassifier(random_state=42)
cart.fit(X_train, y_train)

  • 使用 train_test_split 将数据集划分为训练集和测试集,比例为 80% 训练集和 20% 测试集。
  • 创建一个 DecisionTreeClassifier 实例,并使用训练集 X_trainy_train 进行模型训练。

8. 预测与评估

y_pred = cart.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)

  • 使用训练好的模型在测试集上进行预测,并计算模型的准确率(Accuracy)。

9. 分类报告和特征重要性

print(classification_report(y_test, y_pred))

  • 打印分类报告,显示模型在不同类别上的精确度(Precision)、召回率(Recall)和 F1 值。
for feature, importance in zip(X_encoded_df.columns, cart.feature_importances_):
    print(f"{feature}: {importance:.4f}")

  • 打印每个 One-Hot 编码后的特征在决策树模型中的重要性(Feature Importance)。

10. 可视化决策树

plot_tree(cart, feature_names=list(X_encoded_df.columns), class_names=list(cart.classes_), filled=True, rounded=True)

  • 使用 plot_tree 函数可视化决策树。feature_names 参数用于指定特征名称,class_names 用于指定类别名称,filled=True 使节点根据类别填充颜色,rounded=True 使节点边框变为圆角。

总结

  • 该代码展示了如何使用 One-Hot Encoding 将离散类别特征转换为数值型特征,并使用 DecisionTreeClassifier 进行建模。
  • One-Hot Encoding 是将每个类别特征转化为多个二进制特征(0 或 1),从而使得模型能够处理这些类别型变量。
  • 通过 ColumnTransformer 结合 OneHotEncoder 的方式,可以在一个步骤中完成多个列的 One-Hot 编码,并最终将编码结果用于模型训练和评估。