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