3种特征分箱方法

2023-08-25 11:32:51 浏览数 (1)

特征工程-特征分箱

一般在建立分类模型时,当我们进行特征工程的工作经常需要对连续型变量进行离散化的处理,也就是将连续型字段转成离散型字段。

特征离散化后,模型会更稳定,降低了模型过拟合的风险。离散化的过程中连续型变量重新进行了编码,本文主要介绍是3种常见的特征分箱方法:

分箱特点

  1. 连续型变量执行离散化的分箱操作,能够更加简洁地呈现数据信息
  2. 消除特征变量的量纲影响,因为分箱之后都是类别数,例如:0,1,2…
  3. 能够在一定程度上减少异常值的影响,对异常数据有很强的鲁棒性

模拟数据

模拟一份简单的数据和收入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

参考

  1. 特征离散化(分箱)综述:https://zhuanlan.zhihu.com/p/68865422
  2. 书籍《特征工程入门与实践》
  3. sklearn官网:https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.KBinsDiscretizer.html

0 人点赞