sklearn的自定义转换器transformer以及使用pipeline对数据进行处理

摘要

在很多机器学习场景中,需要我们对数据进行预处理,sklean提供的pipeline接口方便我们将数据预处理与模型训练等工作进行整合,方便对训练集、验证集、测试集做相同的转换操作,极大的提高了工作效率。但是在不同场景下往往预处理的方法会出现多样性,然而sklearn所提供的预处理接口(Transformers)数量有限,有的时候往往需要我们自己编写函数对数据进行预处理。为了让我们自定义的数据预处理函数能够放入sklearn的pipeline中,我们想到了自定义Transformer的方法,本文也将围绕自定义Transformer的具体步骤进行展开。

创建项目

使用编译器:pycharm

创建一个空项目,记得选择好相应的python解释器

创建项目

点击create后创建新项目完成,在空项目文件夹下创建一个python package和一个setup.py文件,python package创建之后会自带一个叫__init__.py的空文件,我们之后会对它进行编写,创建好这些之后整个项目目录会变成下面这样:

项目目录

请注意,我们所创建的这个python package的名字将会作为我们调用这个第三方包时的名字,在这个例子中将是

import transformer

编写自定义sklearn标准转换器

 首先,sklearn为了方便用户自定义预处理过程,提供了TransformerMixin、BaseEstimator等基类,我们可以直接继承过来。另外,pipeline的工作原理是在调用pipeline的fit()方法时逐一调用pipeline中转换器的fit()、transform()方法,再调用最后一步estimator的fit()方法。为此我们需要重载自定义转换器的fit()方法和transform()方法,基类TransformerMixin中包含fit()和transform()方法,基类BaseEstimator中包含获取和设置转换器参数的方法get_params()、set_params()。

本实例将编写一个去掉指定列的转换器,即该转化器接受一个参数,经过fit(),transform()之后得到一个去掉指定列的DataFrame:

我们在刚才创建好的python package下新建一个python文件‘DropColumns.py’,该文件用来定义我们的转换器,文件内容如下:
 

import pandas as pd
import numpy as np
from sklearn.base import BaseEstimator, TransformerMixin
 
class DropColumns(BaseEstimator, TransformerMixin):
    def __init__(self, drop_list):
        self.drop_list = drop_list 

    def fit(self, X, y=None):
        return self 

    def transform(self, X, y=None):
        useful_columns = [x for x in list(X.columns) if (x not in self.drop_list)]
        df = X[useful_columns].copy()
        return df

 该转换器没有参数需要通过fit()方法保存,所以直接在fit()中return self即可。

为了对比,我们将创建一个需要保存参数的转换器IVTransformer,用于根据label计算目标属性的woe映射并计算其信息量iv,我们接着创建一个python文件‘IVTransformer.py’,文件内容如下:

 

import pandas as pd
import numpy as np
from sklearn.base import BaseEstimator, TransformerMixin 

class IVTransformer(BaseEstimator, TransformerMixin):
    def __init__(self, target_column=' ', label_column=' ', positive_label=[0], iv=0, woe=0):
        self.target_column = target_column
        self.label_column = label_column
        self.positive_label = positive_label
        self.iv = iv
        self.woe = woe 

    def fit(self, df_name, y=None):
        if len(df_name[self.label_column].unique()) == 2:
            pos_label, neg_label = df_name.loc[:, self.label_column].unique()
            pos_counts = df_name[df_name[self.label_column] == pos_label][self.target_column].value_counts()
            neg_counts = df_name[df_name[self.label_column] == neg_label][self.target_column].value_counts()
            pos_total = df_name[self.label_column].value_counts()[pos_label]
            neg_total = df_name[self.label_column].value_counts()[neg_label]
            pos_rate = pos_counts / pos_total
            neg_rate = neg_counts / neg_total
            self.woe = np.log(pos_rate / neg_rate)
            self.iv = np.sum((pos_rate - neg_rate) * self.woe)
            return self 

        elif len(df_name[self.label_column].unique()) > 2:
            new_label = np.array(['pos' if x in self.positive_label else 'neg' for x in df_name[self.label_column]])
            pos_total = (new_label == 'pos').sum()
            neg_total = (new_label == 'neg').sum()
            pos_counts = df_name.loc[new_label == 'pos', self.target_column].value_counts()
            neg_counts = df_name.loc[new_label == 'neg', self.target_column].value_counts()
            pos_rate = pos_counts / pos_total
            neg_rate = neg_counts / neg_total
            self.woe = np.log(pos_rate / neg_rate)
            self.iv = np.sum((pos_rate - neg_rate) * self.woe)
            return self 

        elif len(df_name[self.label_column].unique()) < 2:
            print("Label needs at least 2 classes. The calculation cannot be executed.")
            return self 

    def transform(self, df_name):
        df_name.loc[:, self.target_column] = df_name.loc[:, self.target_column].map(self.woe)
        return df_name

需要注意的是,我们通过fit()计算出来的woe和iv将保存在当前transformer中,在调用transform()时将不会改变这两个参数,因此我们在自定义其他转换器时也要注意,不要在transform()方法中改变转换器的参数。

另外,为了能在载入模块时使用*符号来载入模块内所有的类,我们需要在__init__.py文件内加入以下内容:

from .DropColumns import DropColumns
from .IVTransformer import IVTransformer 

__all__ = ['IVTransformer','DropColumns']

 至此我们完成了自定义转换器的工作,但是为了方便大家调用我们的转换器来制作自己的pipeline,我们可以将这些代码打成包,那么接下来我们来完成后续的打包工作。


制作python第三方库

这一步需要定义最开始创建的setup.py文件,内容如下:

from setuptools import setup
VERSION = '1.0.1'
setup(name = 'Transformer',
      version = VERSION,
      description = 'DIY Transformers',
      author='zhj',
      author_email='760007506@qq.com',
      packages=['transformer'],
      zip_safe=False)

 

这里需要注意的是,setup()函数的packages参数需要表明当前目录下包含__init__.py文件的目录,因此如果定义了子模块,需要将子模块包含__init__.py文件的路径添加到packages参数list里面。另外需要注意的是,这里的name参数是安装库时的名字,与调用库时的名字不一样,为了区别,这个参数我们设置成首字母大写的Transformer。

接下来打开终端,进入setup.py文件所在的目录下,输入

python3 setup.py bdist_wheel

回车之后将产生三个文件夹‘build’、‘dist’、‘Transformer.egg-info’,我们打开dist文件夹会看到一个.whl拓展名的文件,这就是我们接下来要用到的安装文件。至此打包工作完成~

库的安装与调用

在指定python环境下打开终端,输入以下内容来安装我们的转换器库
 

pip install .../dist/Transformer-1.0.1-py3-none-any.whl

这里需要输入之前生成的.whl文件的绝对路径,这样我们的库就被安装在环境中了,在终端里输入pip list就可以看到它了。

接下来需要注意的就是,在调用我们的库时需要键入

import transformer

 这里如果transformer首字母大写的话会报错。也就是说,import的模块名字与我们项目文件夹中的python package的名字一致。可以写一个测试脚本来测试我们的库:

from transformer import *
import pandas as pd
import numpy as np
from sklearn.pipeline import Pipeline
import xgboost as xgb
from sklearn import metrics 

if __name__ == '__main__':
######################    load data    ######################
    train_data = pd.read_csv('...')
    vali_data = pd.read_csv('...')
    print(train_data.shape, vali_data.shape)
    print('Train Model...')
    fea_to_drop = [...]

    clf = xgb.XGBClassifier(
        learning_rate =0.1,
        n_estimators=100,
        max_depth=2,
        min_child_weight=1,
        gamma=0,
        subsample=0.7,
        colsample_bytree=0.8,
        objective = 'binary:logistic') 

    train_y = train_data.loc[:, 'label']
    pipeline = Pipeline([
                        ('step_iv',IVTransformer(target_column = 'data_0', label_column = 'label')),
                        ('step_drop',DropColumns(drop_list = fea_to_drop)),
                        ('clf',clf)]) 

    p = pipeline.fit(train_data,train_y)
    print(p.predict(vali_data.loc[0:10]))

 参考:

sklearnAPI文档

基于sklearn的自定义转换器,用于整合到pipeline中实现标准化机器学习流水线 

sklearn中自定义转换器以及使用流水线对数据进行处理 

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值