如何在sklearn中对分类变量进行编码?

12
我正在尝试使用来自UCI存储库的汽车评估数据集,想知道是否有一种方便的方法在sklearn中对分类变量进行二值化。一种方法是使用LabelBinarizer的DictVectorizer,但这里会得到k个不同的特征,而您应该只有k-1个以避免共线性。 我猜我可以编写自己的函数并删除一列,但这种繁琐的记录很麻烦,有没有一种简单的方法来执行这些转换并作为结果获得稀疏矩阵?

你是否有特别的原因偏爱 k-1 个特征而不是 k 个?使用 k 个特征可以使得系数的解释更加容易(例如在线性模型中),并且可能会促进稀疏特征。 - Andreas Mueller
1
我试图找到系数的相关性,但遇到了共线性问题。http://en.wikipedia.org/wiki/Multicollinearity - tonicebrian
我想我对统计方面的事情不够了解,无法看出这会成为一个问题。我想k特征编码在特征相关性方面会比任何其他编码方法都提供更有意义的结果。 - Andreas Mueller
2
使用k个特征会导致一个不可识别的模型。 - Lindon
3个回答

31
如果您的数据是pandas DataFrame类型,那么您可以简单地调用get_dummies函数。假设您的数据框为df,并且您想要每个变量'key'的级别有一个二进制变量,您可以简单地调用以下代码:
pd.get_dummies(df['key'])

然后删除其中的一个虚拟变量,以避免多重共线性问题。希望这可以帮助您...


5
就我个人而言,我更喜欢 Pandas 中的 get_dummies 函数,而不是使用 sklearn 中的 OneHotEncoder 或 DictVectorizer 函数。在 Pandas 中使用 get_dummies 函数通常可以得到更简洁的过程和更少的代码。就我所知,Pandas 更专注于数据分析和预处理,而 sklearn 更专注于重型的“学习”过程。 - luanjunyi
20
get_dummies的缺点是在评分数据集上可能不会生成相同的虚拟列(在尝试对模型进行评分时会给你带来很多复杂性)。 - Chris
2
一种方法是首先将训练和测试DataFrame连接起来,然后应用get_dummies函数,最后分离训练和测试DataFrame。这样,训练和测试数据将具有一致的独热编码。 - weidongxu
当模型处于生产状态时,我们还没有看到测试数据集会发生什么? - Aritra

16

基本方法是

import numpy as np
import pandas as pd, os
from sklearn.feature_extraction import DictVectorizer

def one_hot_dataframe(data, cols, replace=False):
    vec = DictVectorizer()
    mkdict = lambda row: dict((col, row[col]) for col in cols)
    vecData = pd.DataFrame(vec.fit_transform(data[cols].apply(mkdict, axis=1)).toarray())
    vecData.columns = vec.get_feature_names()
    vecData.index = data.index
    if replace is True:
        data = data.drop(cols, axis=1)
        data = data.join(vecData)
    return (data, vecData, vec)

data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada'],
        'year': [2000, 2001, 2002, 2001, 2002],
        'pop': [1.5, 1.7, 3.6, 2.4, 2.9]}

df = pd.DataFrame(data)

df2, _, _ = one_hot_dataframe(df, ['state'], replace=True)
print df2

以下是稀疏格式的执行方法。

import numpy as np
import pandas as pd, os
import scipy.sparse as sps
import itertools

def one_hot_column(df, cols, vocabs):
    mats = []; df2 = df.drop(cols,axis=1)
    mats.append(sps.lil_matrix(np.array(df2)))
    for i,col in enumerate(cols):
        mat = sps.lil_matrix((len(df), len(vocabs[i])))
        for j,val in enumerate(np.array(df[col])):
            mat[j,vocabs[i][val]] = 1.
        mats.append(mat)

    res = sps.hstack(mats)   
    return res

data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada'],
        'year': ['2000', '2001', '2002', '2001', '2002'],
        'pop': [1.5, 1.7, 3.6, 2.4, 2.9]}

df = pd.DataFrame(data)
print df

vocabs = []
vals = ['Ohio','Nevada']
vocabs.append(dict(itertools.izip(vals,range(len(vals)))))
vals = ['2000','2001','2002']
vocabs.append(dict(itertools.izip(vals,range(len(vals)))))

print vocabs

print one_hot_column(df, ['state','year'], vocabs).todense()

3
使用这种方法,你如何处理新数据中的未见过的值? - marbel

15
DictVectorizer是生成分类变量的独热编码的推荐方法;您可以使用`sparse`参数创建一个稀疏CSR矩阵,而不是一个密集的numpy数组。通常我不关心多重共线性,并且我没有注意到我倾向于使用的方法(即LinearSVC、SGDClassifier、基于树的方法)存在问题。
将DictVectorizer修补以删除每个分类特征的一列不应该是一个问题 - 您只需要在`fit`方法结束时从`DictVectorizer.vocabulary`中删除一个术语即可。(欢迎提交拉取请求!)

5
您为什么推荐 DictVectorizer 而不是 OneHotEncoder 类? - Dexter
5
DictVectorizer比OneHotEncoder更通用- OneHotEncoder的输入被限制为代表类别的整数列。DictVectorizer还处理文本类别值。另一方面,如果您一开始就只有整数类别,则OneHotEncoder似乎是最简单的选择。 - Alex A.
1
但是,为了使用DictVectorizer,我需要将数组转换为字典列表,列表中的每个字典对应数组中的一行。这似乎是一个不正规的方法。为什么我不能只是将数组传递给DictVectorizer? - Ben

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