sklearn-pandas

转载请注明,来自:https://www.jianshu.com/p/8959917c6253

使用sklearn+pandas做特征选择时,需要对特征名和索引进行维护,以免丢失或错乱,非常麻烦。于是发现了 sklearn-pandas 这个包,对它的使用文档进行了简单翻译。
github: https://github.com/scikit-learn-contrib/sklearn-pandas
这个模块在Scikit-Learn的机器学习方法和pandas风格的数据框架之间提供了一个桥梁。
具体地说,它提供了一种将DataFrame列映射到变换(transformation)的方法,这些转换将被重新组合到特征中。

安装(Installation)


pip install sklearn-pandas

使用方法(Usage)


Import

sklearn_pandas中导入需要的部分,你可以选择:

  • DataFrameMapper,一个类,用于将panda.DataFrame的列映射到不同的 sklearn 变换。
  • cross_val_score,类似sklearn.cross_validation.cross_val_scor,但在 DataFrame 上工作。

对于以下示例,我们需要导入这两个:

from sklearn_pandas import DataFrameMapper, cross_val_score

我们同时还要用到 pandas, numpy, 和 sklearn:

import pandas as pd
import numpy as np
import sklearn.preprocessing, sklearn.decomposition, \
              \sklearn.linear_model, sklearn.pipeline, sklearn.metrics
from sklearn.feature_extraction.text import CountVectorizer

载入数据

通常会从文件中读取数据,但为了演示目的,我们将利用 dict 创建一个 DataFrame:

data = pd.DataFrame({'pet':      ['cat', 'dog', 'dog', 'fish', 'cat', 'dog', 'cat', 'fish'],
                     'children': [4., 6, 3, 3, 2, 3, 5, 4],
                     'salary':   [90., 24, 44, 27, 32, 59, 36, 27]})

变换映射(Transformation Mapping)


将列(column)映射到变换(Transformation )

映射器(mapper)需要传入一个元组列表(a list of tuples)。每个tuple的第一个元素是DataFrame中的一个列名,或者一个list,这个list包含一个或多个列名。第二个元素是一个object,它将执行应用于该列的变换。第三个元素是可选的(如果可用的话),它是一个包含变换设置选项的dict(请参见下面的“为变换后的特征自定义列名”)。

mapper = DataFrameMapper([
    ('pet', sklearn.preprocessing.LabelBinarizer()),
    (['children'], sklearn.preprocessing.StandardScaler())
 ])

指定列选择器为'column'(作为简单字符串)和['column'](作为带有一个元素的列表)之间的区别是传递给变换器的数组的形状。在第一种情况下,将传递一个一维数组,而在第二种情况下,将会传递一个只有一列的二维数组,即一个列向量。

这种特性模仿了 pandas.DataFrame 的__getitem__索引的模式:

>>> data['children'].shape
(8,)
>>> data[['children']].shape
(8, 1)

注意,一些变换器需要一维输入(以标签为导向的),而另一些变换器(如OneHotEncoderImputer),则需要得到具有形状[n_samples, n_features]的二维输入。

测试变换

我们可以使用fit_transform来喂入模型并查看转换后的数据。在本例和其他示例中,使用np.round将输出四舍五入为两位数,以避免不同硬件上的舍入误差:

>>> np.round(mapper.fit_transform(data.copy()), 2)
array([[ 1.  ,  0.  ,  0.  ,  0.21],
       [ 0.  ,  1.  ,  0.  ,  1.88],
       [ 0.  ,  1.  ,  0.  , -0.63],
       [ 0.  ,  0.  ,  1.  , -0.63],
       [ 1.  ,  0.  ,  0.  , -1.46],
       [ 0.  ,  1.  ,  0.  , -0.63],
       [ 1.  ,  0.  ,  0.  ,  1.04],
       [ 0.  ,  0.  ,  1.  ,  0.21]])

注意,前三列是LabelBinarizer的输出(分别对应于catdogfish),第四列是children数量标准化后的值。通常,输出后的列顺序和构造DataFrameMapper时的列序一致。

现在变换器已经经过了训练,我们来确认它对新数据有效:

>>> sample = pd.DataFrame({'pet': ['cat'], 'children': [5.]})
>>> np.round(mapper.transform(sample), 2)
array([[1.  , 0.  , 0.  , 1.04]])

输出特征名

在某些情况下,比如在研究特征重要性时,我们希望能够将原始特征与DataFrameMapper生成的特征相关联。我们可以通过调用执行变换后 mapper 自动生成的transformed_names_属性来实现:

>>> mapper.transformed_names_
['pet_cat', 'pet_dog', 'pet_fish', 'children']

为变换后的特征自定义列名

我们可以为变换后的特征自定义名称,以代替自动生成的特征名:

>>> mapper_alias = DataFrameMapper([
...     (['children'], sklearn.preprocessing.StandardScaler(),
...      {'alias': 'children_scaled'})
... ])
>>> _ = mapper_alias.fit_transform(data.copy())
>>> mapper_alias.transformed_names_
['children_scaled']

向变换器传递 Series/DataFrames

默认情况下,Series/DataFrames在传递给DataFrameMapper后,选定列将会作为numpy.Array传递给指定的变换器(这是因为sklearn转换器在是基于numpy数组而设计的)。

然而,有些变化器(一般是定制的变换器)需要Series/DataFrames作为输入。这种情况下,我们需要在初始化DataFrameMapper时,使input_df=True

>>> from sklearn.base import TransformerMixin
>>> class DateEncoder(TransformerMixin):
...    def fit(self, X, y=None):
...        return self
...
...    def transform(self, X):
...        dt = X.dt
...        return pd.concat([dt.year, dt.month, dt.day], axis=1)
>>> dates_df = pd.DataFrame(
...     {'dates': pd.date_range('2015-10-30', '2015-11-02')})
>>> mapper_dates = DataFrameMapper([
...     ('dates', DateEncoder())
... ], input_df=True)
>>> mapper_dates.fit_transform(dates_df)
array([[2015,   10,   30],
       [2015,   10,   31],
       [2015,   11,    1],
       [2015,   11,    2]])

我们也可以为单独一列指定这个选项,而不是为整个映射器:

>>> mapper_dates = DataFrameMapper([
...     ('dates', DateEncoder(), {'input_df': True})
... ])
>>> mapper_dates.fit_transform(dates_df)
array([[2015,   10,   30],
       [2015,   10,   31],
       [2015,   11,    1],
       [2015,   11,    2]])

输出 DataFrame

默认情况下,DataFrameMapper 的输出是一个 numpy 数组。这是因为大多数 sklearn 估计器都需要输入一个 numpy 数组。如果我们希望映射器的输出是一个 dataframe,我们可以在创建映射器时使参数df_out=True:

>>> mapper_df = DataFrameMapper([
...     ('pet', sklearn.preprocessing.LabelBinarizer()),
...     (['children'], sklearn.preprocessing.StandardScaler())
... ], df_out=True)
>>> np.round(mapper_df.fit_transform(data.copy()), 2)
   pet_cat  pet_dog  pet_fish  children
0        1        0         0      0.21
1        0        1         0      1.88
2        0        1         0     -0.63
3        0        0         1     -0.63
4        1        0         0     -1.46
5        0        1         0     -0.63
6        1        0         0      1.04
7        0        0         1      0.21

列的名称与transformed_names_属性中的名称相同。
注意,df_out=True参数与Mapper的default=Truesparse=True参数不兼容。

需要多个输入列的变换(如PCA)

变换可能需要多个输入列(如PCA)。在这些情况下,列名可以在一个list中指定:

>>> mapper2 = DataFrameMapper([
...     (['children', 'salary'], sklearn.decomposition.PCA(1))
... ])

接下来执行fit_transform,将会在childrensalary两列使用 PCA,并返回第一个主成分:

>>> np.round(mapper2.fit_transform(data.copy()), 1)
array([[ 47.6],
       [-18.4],
       [  1.6],
       [-15.4],
       [-10.4],
       [ 16.6],
       [ -6.4],
       [-15.4]])

对同一列执行多种转换

通过在一个list中指定多种变换器,可以对同一列执行多种变换:

>>> mapper3 = DataFrameMapper([
...     (['age'], [sklearn.preprocessing.Imputer(),
...                sklearn.preprocessing.StandardScaler()])])
>>> data_3 = pd.DataFrame({'age': [1, np.nan, 3]})
>>> mapper3.fit_transform(data_3)
array([[-1.22474487],
       [ 0.        ],
       [ 1.22474487]])

不需要任何变换的列

输出只保留DataFrameMapper中指定的列。若要保留列但不对其应用任何转换,请使用None作为转换器:

>>> mapper3 = DataFrameMapper([
...     ('pet', sklearn.preprocessing.LabelBinarizer()),
...     ('children', None)
... ])
>>> np.round(mapper3.fit_transform(data.copy()))
array([[1., 0., 0., 4.],
       [0., 1., 0., 6.],
       [0., 1., 0., 3.],
       [0., 0., 1., 3.],
       [1., 0., 0., 2.],
       [0., 1., 0., 3.],
       [1., 0., 0., 5.],
       [0., 0., 1., 4.]])

指定默认变换器

默认变换器可以应用于未显式选择的列,将其作为默认参数传递给 Mapper:

>>> mapper4 = DataFrameMapper([
...     ('pet', sklearn.preprocessing.LabelBinarizer()),
...     ('children', None)
... ], default=sklearn.preprocessing.StandardScaler())
>>> np.round(mapper4.fit_transform(data.copy()), 1)
array([[ 1. ,  0. ,  0. ,  4. ,  2.3],
       [ 0. ,  1. ,  0. ,  6. , -0.9],
       [ 0. ,  1. ,  0. ,  3. ,  0.1],
       [ 0. ,  0. ,  1. ,  3. , -0.7],
       [ 1. ,  0. ,  0. ,  2. , -0.5],
       [ 0. ,  1. ,  0. ,  3. ,  0.8],
       [ 1. ,  0. ,  0. ,  5. , -0.3],
       [ 0. ,  0. ,  1. ,  4. , -0.7]])

使default=False(默认为False)将会丢掉未选择的列。使default=None将不对未选中的列做任何变换地保留他们。

多个列使用相同的变换器

有时需要对几个列应用相同的变换。为了简化这个过程(不用对每个列指定变换器),包提供了gen_features函数,该函数接受一个包含列名的list,和特征变换器类(特征变换器类的列表),返回一个新特征的定义,可以被DataFrameMapper接受。

例如,考虑一个有3个分类列的数据集,分别是'col1'、'col2'和'col3',要对它们进行数值化编码,可以将列名和LabelEncoder变换器类传递到gen_features中,然后使用返回的定义作为DataFrameMapperfeature参数:

>>> from sklearn_pandas import gen_features
>>> feature_def = gen_features(
...     columns=['col1', 'col2', 'col3'],
...     classes=[sklearn.preprocessing.LabelEncoder]
... )
>>> feature_def
[('col1', [LabelEncoder()]), ('col2', [LabelEncoder()]), ('col3', [LabelEncoder()])]
>>> mapper5 = DataFrameMapper(feature_def)
>>> data5 = pd.DataFrame({
...     'col1': ['yes', 'no', 'yes'],
...     'col2': [True, False, False],
...     'col3': ['one', 'two', 'three']
... })
>>> mapper5.fit_transform(data5)
array([[1, 1, 0],
       [0, 0, 2],
       [1, 0, 1]])

如果需要指定变换器的参数,那么应该提供一个带有class键和变换器参数的dict。例如,考虑一个有缺失值的数据集,下面的代码可以用来覆盖默认的输入策略:

>>> feature_def = gen_features(
...     columns=[['col1'], ['col2'], ['col3']],
...     classes=[{'class': sklearn.preprocessing.Imputer, 'strategy': 'most_frequent'}]
... )
>>> mapper6 = DataFrameMapper(feature_def)
>>> data6 = pd.DataFrame({
...     'col1': [None, 1, 1, 2, 3],
...     'col2': [True, False, None, None, True],
...     'col3': [0, 0, 0, None, None]
... })
>>> mapper6.fit_transform(data6)
array([[1., 1., 0.],
       [1., 0., 0.],
       [1., 1., 0.],
       [2., 1., 0.],
       [3., 1., 0.]])

特征选择和其他有监督的变换

DataFrameMapper支持同时需要X和y参数的变换器,比如特征选择。将 'pet' 列作为标签,我们将选择最能预测它的列:

>>> from sklearn.feature_selection import SelectKBest, chi2
>>> mapper_fs = DataFrameMapper([(['children','salary'], SelectKBest(chi2, k=1))])
>>> mapper_fs.fit_transform(data[['children','salary']], data['pet'])
array([[90.],
       [24.],
       [44.],
       [27.],
       [32.],
       [59.],
       [36.],
       [27.]])

处理稀疏特征

默认情况下,DataFrameMapper将返回一个密集的特征数组。在 Mapper 中设置sparse=True将在提取的任何特征为稀疏时,返回一个稀疏数组:

>>> mapper5 = DataFrameMapper([
...     ('pet', CountVectorizer()),
... ], sparse=True)
>>> type(mapper5.fit_transform(data))
<class 'scipy.sparse.csr.csr_matrix'>

在不致密的情况下,完成对稀疏特征的堆叠。

交叉验证

现在我们已经结合了pandas.DataFrame的特性,我们还可能想要使用交叉验证来验证我们的模型是否有效。scikit-learn<0.16.0提供了支持交叉验证的特性,但是期望使用numpy数据结构作为输入,并且不能直接使用DataFrameMapper

为了解决这个问题,sklearn-pandas 为 sklearn 的cross_val_score函数提供了一个包装器(wrapper),它将一个panda.DataFrame传递给估计器,而不是一个numpy数组:

>>> pipe = sklearn.pipeline.Pipeline([
...     ('featurize', mapper),
...     ('lm', sklearn.linear_model.LinearRegression())])
>>> np.round(cross_val_score(pipe, X=data.copy(), y=data.salary, scoring='r2'), 2)
array([ -1.09,  -5.3 , -15.38])

Sklearn-panda的cross_val_score函数提供了与 sklearn 同名函数完全相同的接口。

CategoricalImputer

由于scikit-learn Imputer变换器目前只能处理数字,因此 sklearn-panda 提供了一个等效的辅助变换器,它可以处理字符串,用该列中最常见的值替换空值。或者,您可以指定要使用的固定值。
Example: imputing with the mode:

>>> from sklearn_pandas import CategoricalImputer
>>> data = np.array(['a', 'b', 'b', np.nan], dtype=object)
>>> imputer = CategoricalImputer()
>>> imputer.fit_transform(data)
array(['a', 'b', 'b', 'b'], dtype=object)

Example: imputing with a fixed value:

>>> from sklearn_pandas import CategoricalImputer
>>> data = np.array(['a', 'b', 'b', np.nan], dtype=object)
>>> imputer = CategoricalImputer(strategy='constant', fill_value='a')
>>> imputer.fit_transform(data)
array(['a', 'b', 'b', 'a'], dtype=object)

FunctionTransformer

通常,我们希望对诸如np.log这样的数据应用简单的转换。FunctionTransformer是一个简单的包装器(wrapper),它接受任何函数并进行向量化,以便可以将其用作变换器。这样,就可以与DataFrameMapper结合使用
Example:

>>> from sklearn_pandas import FunctionTransformer
>>> array = np.array([10, 100])
>>> transformer = FunctionTransformer(np.log10)

>>> transformer.fit_transform(array)
array([1., 2.])

转载请注明,来自:https://www.jianshu.com/p/8959917c6253

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,504评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,434评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,089评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,378评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,472评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,506评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,519评论 3 413
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,292评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,738评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,022评论 2 329
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,194评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,873评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,536评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,162评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,413评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,075评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,080评论 2 352

推荐阅读更多精彩内容