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 编码,并最终将编码结果用于模型训练和评估。