NYC's Blog - scikit-learn http://niyanchun.com/tag/scikit-learn/ scikit-learn的ColumnTransformer和OneHotEncoder http://niyanchun.com/sklearn-columntransformer-and-onehotencoder.html 2018-10-11T21:41:00+08:00 本文介绍scikit-learn 0.20版本中新增的sklearn.compose.ColumnTransformer和有所改动的sklearn.preprocessing.OneHotEncoder。ColumnTransformer假设现在有这样一个场景:有一个数据集,每个样本包含n个数值型(numeric)特征,m个标称型(categorical)特征,我们在使用这个数据集训练模型之前,需要对n个数值型特征做归一化,对m个标称型特征做one-hot编码?这个要如何实现?其实这个不难,但挺麻烦的。一般的方式是把数值型的特征数据列和标称型数据分别拿出来,然后分别做预处理,处理完之后再拼在一起训练模型。这样一方面是麻烦,另一方面比较难保证原来特征的顺序(虽然顺序一般没什么影响)。scikit-learn在0.20.0版本中新增了一个sklearn.compose.ColumnTransformer类,通过这个类我们可以对输入的特征分别做不同的预处理,并且最终的结果还在一个特征空间里面。描述太抽象,直接看官方的一个例子:# Author: Pedro Morales <part.morales@gmail.com> # # License: BSD 3 clause from __future__ import print_function import pandas as pd import numpy as np from sklearn.compose import ColumnTransformer from sklearn.pipeline import Pipeline from sklearn.impute import SimpleImputer from sklearn.preprocessing import StandardScaler, OneHotEncoder from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split, GridSearchCV np.random.seed(0) # Read data from Titanic dataset. titanic_url = ('https://raw.githubusercontent.com/amueller/' 'scipy-2017-sklearn/091d371/notebooks/datasets/titanic3.csv') data = pd.read_csv(titanic_url) # We will train our classifier with the following features: # Numeric Features: # - age: float. # - fare: float. # Categorical Features: # - embarked: categories encoded as strings {'C', 'S', 'Q'}. # - sex: categories encoded as strings {'female', 'male'}. # - pclass: ordinal integers {1, 2, 3}. # We create the preprocessing pipelines for both numeric and categorical data. numeric_features = ['age', 'fare'] numeric_transformer = Pipeline(steps=[ ('imputer', SimpleImputer(strategy='median')), ('scaler', StandardScaler())]) categorical_features = ['embarked', 'sex', 'pclass'] categorical_transformer = Pipeline(steps=[ ('imputer', SimpleImputer(strategy='constant', fill_value='missing')), ('onehot', OneHotEncoder(handle_unknown='ignore'))]) preprocessor = ColumnTransformer( transformers=[ ('num', numeric_transformer, numeric_features), ('cat', categorical_transformer, categorical_features)]) # Append classifier to preprocessing pipeline. # Now we have a full prediction pipeline. clf = Pipeline(steps=[('preprocessor', preprocessor), ('classifier', LogisticRegression(solver='lbfgs'))]) X = data.drop('survived', axis=1) y = data['survived'] X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2) clf.fit(X_train, y_train) print("model score: %.3f" % clf.score(X_test, y_test)) # output: # model score: 0.790例子很简单,里面的注释已经表述的比较清楚了:读入了titanic3.csv数据集,里面包含了2个数值型特征(age, fare)和3个标称型特征(embarked, sex, pclass),然后对数值型特征做缺失值处理和归一化,对标称型特征做缺失值处理和One-Hot编码。例子里面使用Pipeline将这些操作串了起来。我们看下sklearn.compose.ColumnTransformer的原型:class sklearn.compose.ColumnTransformer(transformers, remainder=’drop’, sparse_threshold=0.3, n_jobs=None, transformer_weights=None)简单介绍一下transformers和remainder两个参数:transformers:该参数是一个由元组组成的列表(list of tuples),每个元组的结构为:(name, transformer, column):name: transformer的名字,随便起一个字符串即可;transformer: 支持fit和transform的estimator或者passthrough或者drop. passthrough表示透传,不对column指定的列做任何转换;drop表示丢弃指定column指定的列。column: 指定对哪些列做转换操作,所以可以是下标、列名等。remainder:这个参数的值可以是支持fit和transform的estimator或者passthrough或者drop,默认值是drop,其功能和transformers参数非常像:drop:表示将column指定的列之外的其他列都丢弃;passthrough:表示将column指定的列之外的其他列透传;estimator:表示对column指定的列之外的其他列执行该estimator代表的转换。新功能的使用还是非常容易的。OneHotEncoderscikit-learn 0.20版本里面另外一个比较重要的改动就是sklearn.preprocessing.OneHotEncoder除了支持整数外,还支持字符串。这样如果特征是字符串,就省去了原来需要做sklearn.preprocessing.LabelEncoder的步骤。老的sklearn.preprocessing.OneHotEncoder原型:class sklearn.preprocessing.OneHotEncoder(n_values=’auto’, categorical_features=’all’, dtype=<class ‘numpy.float64’>, sparse=True, handle_unknown=’error’)新的sklearn.preprocessing.OneHotEncoder原型:class sklearn.preprocessing.OneHotEncoder(n_values=None, categorical_features=None, categories=None, sparse=True, dtype=<class ‘numpy.float64’>, handle_unknown=’error’)可以看到新老API的主要差别是新API增加了一个categories参数,这个参数是为了替换里面的n_values参数;后者在0.22版本中就去掉了。而且如果要OneHotEncoder支持字符串的话,就必须使用categories,不能使用n_values了。我们简单介绍一下categories这个参数,该参数的可取值为:'auto'( 默认值,表示根据训练数据自己决定categories;)或一个list of list/array。前者很容易理解,后者稍微难理解一些,我们通过例子来看。>>> enc = preprocessing.OneHotEncoder() >>> X = [['male', 'from US', 'uses Safari'], ['female', 'from Europe', 'uses Firefox']] >>> enc.fit(X) OneHotEncoder(categorical_features=None, categories=None, dtype=<... 'numpy.float64'>, handle_unknown='error', n_values=None, sparse=True) >>> enc.transform([['female', 'from US', 'uses Safari'], ... ['male', 'from Europe', 'uses Safari']]).toarray() array([[1., 0., 0., 1., 0., 1.], [0., 1., 1., 0., 0., 1.]]) # 可以通过categories_属性查看所有类别 >>> enc.categories_ [array(['female', 'male'], dtype=object), array(['from Europe', 'from US'], dtype=object), array(['uses Firefox', 'uses Safari'], dtype=object)]上例中,categories采用默认值,如果我们需要使用list实现等效的效果的话,可以按照如下方式改写上面代码:>>> enc_new = preprocessing.OneHotEncoder(categories=[['male', 'female'],['from Europe', 'from US'],['uses Firefox', 'uses Safari']]) >>> enc_new.fit(X) OneHotEncoder(categorical_features=None, categories=[['male', 'female'], ['from Europe', 'from US'], ['uses Firefox', 'uses Safari']], dtype=<class 'numpy.float64'>, handle_unknown='error', n_values=None, sparse=True) >>> enc_new.transform([['female', 'from US', 'uses Safari'], ... ['male', 'from Europe', 'uses Safari']]).toarray() array([[1., 0., 0., 1., 0., 1.], [0., 1., 1., 0., 0., 1.]]) >>> enc_new.categories_ [array(['male', 'female'], dtype=object), array(['from US', 'from Europe'], dtype=object), array(['uses Safari', 'uses Firefox'], dtype=object)]例子已经展示的很清楚了,categories的值取list of list/array时候,里面的categories[i]表示第i列特征的categories。同时需要注意:在单个特征的list/array里面,其值要么是numeric要么是string,不能混用;如果是numeric,还需要是排序的。Reference:scikit-learn.org