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


78

关于如何将分类数据编码到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传递分类数据,这可能吗?

Answers:


-5

与接受的答案相反,我更愿意使用Scikit-Learn提供的工具来实现此目的。这样做的主要原因是它们可以轻松集成到管道中

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该类对此提供支持。

希望这可以帮助!


152
-1这是令人误解的。就目前而言,sklearn决策树不处理分类数据-请参阅问题#5442。这种使用标签编码的方法将转换为DecisionTreeClassifier() 将被视为数字的整数。如果您的分类数据不是序数,那将是不好的-您将得到没有意义的拆分。使用aOneHotEncoder是当前唯一有效的方法,但计算量大。
James Owers

@Abhinav,是否可以一次将其应用于LabelEncoder数据框的多列?例如,在问题的数据框中,我们可以做一些事情le.fit_transform(data[['A','B','C']])来一次获取所有分类列的标签吗?或者应该明确指定分类列以仅转换分类列。
米奴

21
这是极具误导性的。请不要将字符串转换为数字并在决策树中使用。在scikit-learn中无法处理分类数据。一种选择是在Spark中使用决策树分类器-您可以在其中明确声明分类特征及其序数。有关更多详细信息,请参见此处github.com/scikit-learn/scikit-learn/pull/4899
Pradeep Banavara

6
每个人都必须学习度量标度,即标称,有序,间隔和比率标度。数字并不意味着它在名义尺度上是数字。它只是一个标志。例如,我们可以使用1表示红色,2表示蓝色,3表示绿色。假设有10个人喜欢红色,而10个人喜欢绿色。计算平均值((10 * 1 + 10 * 3)/ 20 = 2)并声明平均偏爱Blue是有意义的吗?
雷吉·马修

2
嗯...我不知道它有那么多关注。欢呼@ayorgo,会的!
James Owers

61

(这只是从2016年开始的上述评论的格式,它仍然成立。)

该问题的公认答案具有误导性。

就目前而言,sklearn决策树不处理分类数据-请参阅问题#5442

建议的使用“标签编码”的方法将转换为DecisionTreeClassifier()将被视为数字的整数。如果您的分类数据不是序数,那将是不好的-您将得到没有意义的拆分。

使用aOneHotEncoder是当前唯一有效的方法,它允许不依赖标签顺序的任意分割,但是计算量大。


3
OneHotEncoding显然会导致决策树的性能下降,因为它会导致极其稀疏的功能,从而破坏
Arun

1
同意-我不推荐这种方法,但这是避免我目前描述的问题的唯一方法。
James Owers

我怀疑在某些情况下(具有许多小级别的特征),在普通编码的分类特征上进行“无意义”分割仍会比在单热编码特征上进行非常有限的分割产生更好的性能。
本·赖尼格

决策树分类器还有其他实现可以解决吗?
调制解调器Rakesh钉

12

(..)

能够处理数字和分类数据。

这仅意味着您可以使用

  • 分类问题的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'])

2
您可能想玩'pd.get_dummies',例如选项'drop_first = True'可以帮助避免多重共线性问题。这里有一个很好的教程。
拉斐尔·瓦莱罗

4

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


2

Sklearn决策树不处理分类字符串到数字的转换。我建议您在Sklearn(也许是)中找到一个可以执行此操作的函数,或手动编写一些代码,例如:

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

是的,这通常是水,但是对于印刷来说并不是很好。
0xhfff 2016年

如果要从整数返回到字符串表示形式,请制作一个字典,其中包含字符串和整数之间的映射,并使用该映射“解码”整数表示形式。
mrwyatt '16

1
该说法不准确。Scikit学习分类器不会隐式处理标签编码。但是,Scikit-learn提供了许多类来处理此问题。我建议使用scikit学习工具,因为它们也可以以最小的努力安装在机器学习管道中。
Abhinav Arora
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.