将分类数据传递给Sklearn决策树

114

有关如何将分类数据编码为Sklearn决策树的文章很多,但我们从Sklearn文档中得知:

决策树的一些优点包括:

(...)

能够处理数值和分类数据。其他技术通常专门分析只有一种类型变量的数据集。请参阅算法以获得更多信息。

但运行以下脚本:

import pandas as pd 
from sklearn.tree import DecisionTreeClassifier

data = pd.DataFrame()
data['A'] = ['a','a','b','a']
data['B'] = ['b','b','a','b']
data['C'] = [0, 0, 1, 0]
data['Class'] = ['n','n','y','n']

tree = DecisionTreeClassifier()
tree.fit(data[['A','B','C']], data['Class'])

输出以下错误:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python2.7/site-packages/sklearn/tree/tree.py", line 154, in fit
    X = check_array(X, dtype=DTYPE, accept_sparse="csc")
  File "/usr/local/lib/python2.7/site-packages/sklearn/utils/validation.py", line 377, in check_array
    array = np.array(array, dtype=dtype, order=order, copy=copy)
ValueError: could not convert string to float: b

我知道在R中可以传递分类数据,那么在Sklearn中是否也可以呢?


SE网络中的相关问题:数据科学中的问题32622数据科学中的问题5226 - Carlos Pinzón
9个回答

110

(这只是我在2016年的评论的重新格式化...它仍然是正确的。)

这个问题的被接受的答案是误导性的。

目前,sklearn决策树不能处理分类数据 - 请参见问题#5442

使用标签编码的推荐方法将转换为整数,DecisionTreeClassifier()将把其视为数字。如果您的分类数据不是有序的,则不好 - 您最终会得到毫无意义的分裂。

使用OneHotEncoder是目前唯一有效的方法,允许任意拆分而不依赖于标签顺序,但计算成本很高。


7
OneHotEncoding 可能会降低决策树的性能,因为它会导致特征变得极其稀疏,从而混淆特征重要性。 (来源:https://roamanalytics.com/2016/10/28/are-categorical-variables-getting-lost-in-your-random-forests/) - Arun
3
同意 - 我不建议这种方法,但目前这是避免我所描述的问题的唯一途径。 - James Owers
2
我怀疑在具有许多小级别特征的情况下,“无意义”的分割在序数编码的分类特征上产生的性能比单热编码特征上的非常有限的分割更好。 - Ben Reiniger
1
有没有其他能够处理这个问题的决策树分类器实现? - Modem Rakesh goud
2
更新:这个Pull Request(以及其中的讨论)可能会引起兴趣:https://github.com/scikit-learn/scikit-learn/pull/12866 - James Owers
显示剩余3条评论

25
能够处理数值和分类数据。
这意味着您可以使用:
- 用于分类问题的DecisionTreeClassifier类 - 用于回归问题的DecisionTreeRegressor类。
无论哪种情况,在使用sklearn拟合树之前,您需要对分类变量进行独热编码,如下所示:
import pandas as pd
from sklearn.tree import DecisionTreeClassifier

data = pd.DataFrame()
data['A'] = ['a','a','b','a']
data['B'] = ['b','b','a','b']
data['C'] = [0, 0, 1, 0]
data['Class'] = ['n','n','y','n']

tree = DecisionTreeClassifier()

one_hot_data = pd.get_dummies(data[['A','B','C']],drop_first=True)
tree.fit(one_hot_data, data['Class'])

3
你可能想尝试使用“pd.get_dummies”函数进行操作,例如选项“drop_first = True”可帮助避免多重共线性问题。这里有一个不错的教程视频。 - Rafael Valero

9

对于名义分类变量,我不会使用 LabelEncoder,而是使用 sklearn.preprocessing.OneHotEncoderpandas.get_dummies,因为这些类型的变量通常没有顺序。


6
截至 v0.24.0 版本,scikit 支持在 HistGradientBoostingClassifierHistGradientBoostingRegressornatively使用分类特征!

To enable categorical support, a boolean mask can be passed to the categorical_features parameter, indicating which feature is categorical. In the following, the first feature will be treated as categorical and the second feature as numerical:

>>> gbdt = HistGradientBoostingClassifier(categorical_features=[True, False])

Equivalently, one can pass a list of integers indicating the indices of the categorical features:

>>> gbdt = HistGradientBoostingClassifier(categorical_features=[0])
你仍需要对字符串进行编码,否则会出现“无法将字符串转换为浮点数”的错误。请查看此处,了解使用OrdinalEncoder将字符串转换为整数的示例。

1
抱歉问一个愚蠢的问题,这与决策树有关吗?如果是的话,您能否提供一个示例,说明我们如何使用分类变量与决策树(我是个新手...)? - umbe1987
1
这是梯度提升。OP正在询问决策树。 - remykarem

2

是的,决策树可以处理数值和分类数据。这一点在理论部分是正确的,但在实现过程中,您应该在训练或测试模型之前尝试使用OrdinalEncoderone-hot-encoding来处理分类特征。请记住,机器学习模型只能理解数字,而对其他内容一无所知。


2
机器学习模型也不懂英语。 - Adarsh Wase
理论上,决策树可以处理分类输入数据。 - Swapnil Masurekar

1

Sklearn决策树无法将分类字符串转换为数字。我建议您在Sklearn中找到一个函数(可能是这个),可以自动完成转换,或手动编写代码,例如:

def cat2int(column):
    vals = list(set(column))
    for i, string in enumerate(column):
        column[i] = vals.index(string)
    return column

是的,那通常是我做的,但对于打印来说并不是很好。 - 0xhfff
1
如果你想从整数转换回字符串表示,可以创建一个字典来保存字符串和整数之间的映射关系,并使用它来“解码”整数表示。 - mrwyatt
2
该说法不准确。Scikit-learn分类器不会隐式处理标签编码。然而,Scikit-learn提供了许多类来处理这个问题。我建议使用Scikit learn工具,因为它们也可以轻松地适应机器学习管道。 - Abhinav Arora

0

-1

使用sklearn分类器,您可以将分类变量建模为输入和输出。

假设您有分类预测变量和分类标签(即多类分类任务)。此外,您希望处理预测变量和标签的缺失或未知标签。

首先需要像OrdinalEncoder这样的编码器。

基本示例:

# encoders
from sklearn.preprocessing import OrdinalEncoder

input_enc = OrdinalEncoder(unknown_value=-1, handle_unknown='use_encoded_value', encoded_missing_value=-1)
output_enc = OrdinalEncoder(unknown_value=-1, handle_unknown='use_encoded_value', encoded_missing_value=-1 )

input_enc.fit(df[['Attribute A','Attribute B']].values)
output_enc.fit(df[['Label']].values)


# build classifier
from sklearn.tree import DecisionTreeClassifier
clf = DecisionTreeClassifier(random_state=0)

X = input_enc.transform(df[['Attribute A','Attribute B']].values)
Y = output_enc.transform(df[['Label']].values)

clf.fit(X, Y)

# predict

predicted = clf.predict(input_enc.transform([('Value 1', 'Value 2')]))
predicted_label = output_enc.inverse_transform([predicted])

如果您使用df[...].values,则编码器将不会存储属性名称(列名)。只要在enc.transform()enc.inverse_transofrm()中使用相同的格式即可(否则会出现警告)。

OrdinalEncoder默认情况下不处理nan值,并且它们不会被cls.fit()处理。这可以通过encoded_missing_value参数解决。

在预测阶段,默认情况下,当编码器要求转换未知标签时,它会抛出错误。这可以通过handle_unknown参数处理。


我看到这个答案被讨厌了,尽管它遵循了官方的scikit-learn文档:https://scikit-learn.org/stable/modules/preprocessing.html#preprocessing-categorical-features - Pawel

-7

我更喜欢使用Scikit-Learn提供的工具来完成这个任务。这样做的主要原因是它们可以很容易地集成到Pipeline中。

Scikit-Learn本身提供了非常好的类来处理分类数据。您应该使用LabelEncoder,而不是编写自定义函数,它是专门为此目的设计的

请参考文档中的以下代码:

from sklearn import preprocessing
le = preprocessing.LabelEncoder()
le.fit(["paris", "paris", "tokyo", "amsterdam"])
le.transform(["tokyo", "tokyo", "paris"]) 

这将自动将它们编码为数字,以供机器学习算法使用。现在,它还支持从整数恢复为字符串。只需调用inverse_transform即可实现此功能:

list(le.inverse_transform([2, 2, 1]))

这将返回['tokyo', 'tokyo', 'paris']

另外请注意,对于许多其他分类器(除了决策树之外),例如逻辑回归或SVM,您希望使用One-Hot编码对分类变量进行编码。Scikit-learn也支持此操作,通过OneHotEncoder类实现。


178
这段话存在误导。目前为止,sklearn决策树无法处理分类数据 - [请参见问题#5442](https://github.com/scikit-learn/scikit-learn/issues/5442)。使用标签编码的方法会将分类数据转换为整数,而`DecisionTreeClassifier()`会将其视为数字进行处理。如果你的分类数据不是有序的,这样做并不好 - 你最终将得到没有意义的分裂。使用OneHotEncoder是目前唯一可用的方式,但计算量昂贵。 - James Owers
23
这是极具误导性的。请不要将字符串转换为数字并在决策树中使用。Scikit-learn无法处理分类数据。一个选择是在Spark中使用决策树分类器,其中您可以明确声明分类特征及其序数。有关更多详细信息,请参见此处 https://github.com/scikit-learn/scikit-learn/pull/4899 - Pradeep Banavara
7
每个人都必须学习测量尺度,即名义、序数、区间和比例尺度。在名义尺度中,数字并不意味着是数值,它只是一个标志。例如,我们可以使用1表示红色,2表示蓝色,3表示绿色。假设有10个人喜欢红色,10个人喜欢绿色。计算平均值((101+103)/20 = 2),并宣称平均倾向于蓝色,这种说法是否有意义? - Regi Mathew
2
额...我不知道它会引起这么多关注。感谢@ayorgo,我会的! - James Owers
1
这就是为什么我的实习生候选人不知道如何处理分类变量的原因。 - Daniel Severo
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接