特征工程-特征分箱
一般在建立分类模型时,当我们进行特征工程的工作经常需要对连续型变量进行离散化的处理,也就是将连续型字段转成离散型字段。
特征离散化后,模型会更稳定,降低了模型过拟合的风险。离散化的过程中连续型变量重新进行了编码,本文主要介绍是3种常见的特征分箱方法:
分箱特点
- 连续型变量执行离散化的分箱操作,能够更加简洁地呈现数据信息
- 消除特征变量的量纲影响,因为分箱之后都是类别数,例如:0,1,2…
- 能够在一定程度上减少异常值的影响,对异常数据有很强的鲁棒性
模拟数据
模拟一份简单的数据和收入INCOME相关
In [1]:
代码语言:javascript复制import pandas as pd
import numpy as np
In [2]:
代码语言:javascript复制df = pd.DataFrame({"ID":range(10),
"INCOME":[0,10,20,150,35,78,50,49,88,14]})
df
sklearn之KBinsDiscretizer类
本文中介绍的3种分箱操作都是基于sklearn中的KBinsDiscretizer类,官网学习地址:
https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.KBinsDiscretizer.html
代码语言:javascript复制from sklearn.preprocessing import KBinsDiscretizer
sklearn.preprocessing.KBinsDiscretizer(n_bins=5,
encode='onehot',
strategy='quantile',
dtype=None,
subsample='warn',
random_state=None)
全部参数解释:
全部属性信息:
重点解释3个参数的使用:
n_bins
参数n_bins参数上指定需要分箱的个数,默认是5个
strategy
指定不同的分箱策略strategy:KBinsDiscretizer类实现了不同的分箱策略,可以通过参数strategy进行选择:
- 等宽:
uniform
策略使用固定宽度的bins;箱体的宽度一致 - 等频:
quantile
策略在每个特征上使用分位数(quantiles)值以便具有相同填充的bins - 聚类:
kmeans
策略基于在每个特征上独立执行的k-means聚类过程定义bins。
encode
encode参数表示分箱后的离散字段是否需要进一步进行独热编码或者其他编码处理
KBinsDiscretizer
类只能识别列向量,需要将DataFrame的数据进行转化:
In [3]:
代码语言:javascript复制income = np.array(df["INCOME"].tolist()).reshape(-1,1)
income
Out[3]:
代码语言:javascript复制array([[ 0],
[ 10],
[ 20],
[150],
[ 35],
[ 78],
[ 50],
[ 49],
[ 88],
[ 14]])
使用之前先导进来:
In [4]:
代码语言:javascript复制from sklearn.preprocessing import KBinsDiscretizer
等宽分箱
所谓的等宽分箱就是将数据分成等宽的几份,比如模拟数据中INCOME的范围是0-150。现在将其等宽分成3份,那么每一份对应的取值范围是:[0,50),[50,100)[100,150]
In [5]:
代码语言:javascript复制from sklearn.preprocessing import KBinsDiscretizer
dis = KBinsDiscretizer(n_bins=3,
encode="ordinal",
strategy="uniform"
)
dis
Out[5]:
代码语言:javascript复制KBinsDiscretizer(encode='ordinal', n_bins=3, strategy='uniform')
In [6]:
代码语言:javascript复制label_uniform = dis.fit_transform(income) # 转换器
label_uniform
Out[6]:
代码语言:javascript复制array([[0.],
[0.],
[0.],
[2.],
[0.],
[1.],
[1.],
[0.],
[1.],
[0.]])
等宽分箱的边界查看:
In [7]:
代码语言:javascript复制dis.bin_edges_
Out[7]:
代码语言:javascript复制array([array([ 0., 50., 100., 150.])], dtype=object)
In [8]:
代码语言:javascript复制dis.n_bins
Out[8]:
代码语言:javascript复制3
等频分箱
等频分箱指的是每个区间内包含的取值个数是相同的,和等宽分箱的区别:
- 等频分箱:每个区间内包括的值一样多,pd.qcut
- 等宽分箱:每两区间之间的距离是一样的,pd.cut
在实施等频分箱之前,我们需要先对数据进行升序排列,然后取中间值进行分箱
In [9]:
代码语言:javascript复制# 1、先排序
sort_df = sorted(df["INCOME"])
sort_df
Out[9]:
代码语言:javascript复制[0, 10, 14, 20, 35, 49, 50, 78, 88, 150]
分成2个类别
In [10]:
代码语言:javascript复制# 2、中间值:35和49的均值
(35 49) / 2
Out[10]:
代码语言:javascript复制42.0
下面我们以42作为等频分箱的依据:
In [11]:
代码语言:javascript复制dis = KBinsDiscretizer(n_bins=2,
encode="ordinal",
strategy="quantile"
)
dis.fit_transform(income) # 转换器
Out[11]:
代码语言:javascript复制array([[0.],
[0.],
[0.],
[1.],
[0.],
[1.],
[1.],
[1.],
[1.],
[0.]])
In [12]:
代码语言:javascript复制dis.bin_edges_
Out[12]:
代码语言:javascript复制array([array([ 0., 42., 150.])], dtype=object)
分成3个类别
总共是10个元素,分成3个类,10/3=3...1
,前面两个3个元素,最后一个是4个元素,即最后一个箱体会包含余数部分的元素:
In [13]:
代码语言:javascript复制dis = KBinsDiscretizer(n_bins=3,
encode="ordinal",
strategy="quantile"
)
label_quantile = dis.fit_transform(income) # 转换器
label_quantile
Out[13]:
代码语言:javascript复制array([[0.],
[0.],
[1.],
[2.],
[1.],
[2.],
[2.],
[1.],
[2.],
[0.]])
In [14]:
代码语言:javascript复制dis.bin_edges_ # 分箱边界
Out[14]:
代码语言:javascript复制array([array([ 0., 20., 50., 150.])], dtype=object)
In [15]:
代码语言:javascript复制sort_df # 排序后的数据
Out[15]:
代码语言:javascript复制[0, 10, 14, 20, 35, 49, 50, 78, 88, 150]
聚类分箱
聚类分箱指的是先对连续型变量进行聚类,然后所属样本的类别作为标识来代替原来的数值。
In [16]:
代码语言:javascript复制from sklearn import cluster
In [17]:
代码语言:javascript复制kmeans = cluster.KMeans(n_clusters=3)
kmeans.fit(income)
Out[17]:
代码语言:javascript复制KMeans(n_clusters=3)
聚类完成后查看每个样本所属的类别:
In [18]:
代码语言:javascript复制kmeans.labels_
Out[18]:
代码语言:javascript复制array([1, 1, 1, 2, 1, 0, 0, 0, 0, 1], dtype=int32)
使用KBinsDiscretizer来实施聚类分箱:
In [19]:
代码语言:javascript复制dis = KBinsDiscretizer(n_bins=3,
encode="ordinal",
strategy="kmeans"
)
label_kmeans = dis.fit_transform(income) # 转换器
label_kmeans
Out[19]:
代码语言:javascript复制array([[0.],
[0.],
[0.],
[2.],
[0.],
[1.],
[0.],
[0.],
[1.],
[0.]])
In [20]:
代码语言:javascript复制dis.bin_edges_ # 分箱边界
Out[20]:
代码语言:javascript复制array([array([ 0. , 54.21428571, 116.5 , 150. ])],
dtype=object)
3种方法对比
In [21]:
代码语言:javascript复制df["label_uniform"] = label_uniform
df["label_quantile"] = label_quantile
df["label_kmeans"] = label_kmeans
df
参考
- 特征离散化(分箱)综述:https://zhuanlan.zhihu.com/p/68865422
- 书籍《特征工程入门与实践》
- sklearn官网:https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.KBinsDiscretizer.html