进击的橘子猫正式改名上线啦!
我的CSDN主页:https://blog.csdn.net/Orange_Spotty_Cat
也欢迎大家搜索微信公众号“进击的橘子猫”,我也会定期分享数据科学、Python、大数据、项目管理与PPT的相关知识。
让我们进击起来吧!
简介
-
本篇主要介绍机器学习建模中数据预处理步骤的数据分箱部分。在本篇中,会对数据分箱的定义、目的、方法分类以及Python的实现方法进行介绍。
一句话概括版
分箱就是把数据按特定的规则进行分组,实现数据的离散化,增强数据稳定性,减少过拟合风险。逻辑回归中进行分箱是非常必要的,其他树模型可以不进行分箱。
分箱的定义
数据分箱(Binning)作为数据预处理的一部分,也被称为离散分箱或数据分段。其实分箱的概念其实很好理解,它的本质上就是把数据进行分组。
我们以年龄为例,看下面的表格,左边是原始年龄数据,右边是分箱后的数据:
年龄(原始数据) |
年龄(分箱后) |
29 |
18至40岁 |
7 |
18岁以下 |
49 |
40至60岁 |
12 |
18岁以下 |
50 |
40至60岁 |
34 |
18至40岁 |
36 |
18至40岁 |
75 |
60岁以上 |
61 |
60岁以上 |
20 |
18至40岁 |
3 |
18岁以下 |
11 |
18岁以下 |
年龄字段中的数据被分到了以下4个组别中:
-
18岁以下
-
18至40岁
-
40至60岁
-
60岁以上
这样,原本取值是任何0-120整数的年龄数据就被分到了上面这4个固定的值中。(笔者有话说:这个120的上限只是示意啊,我当然希望未来大家都能活到几百岁…)
这里补充一句,虽然在实际建模中,分箱一般都是针对连续型数据(如价格、销量、年龄)进行的。但是从理论上,分箱也可以对分类型数据进行。比如,有些分类型数据可取的值非常多,像中国的城市这种数据,这种情况下可以通过分箱,让已经是离散型的数据变得更加离散,比如,城市可以被划分为一级城市、二级城市、三级城市,或者把垃圾分为有害垃圾、可回收垃圾、湿垃圾、干垃圾等等。
所以经过分箱后的数据,有以下两个特点:
-
数据可取值的范围变小了
-
数据的可取值会更加确定与稳定
-
数据中信息会变得模糊,不再那么精确
那么我们为什么要做这样的分箱操作呢?
分箱的原因
在建立逻辑回归模型的过程中,基本都会对特征进行分箱的操作。有些树模型,虽然不是必须,也会对一些特征进行一些分箱,这里主要的原因是增强鲁棒性与避免过拟合。
从前面的例子可以看出来,当把数据进行分箱处理后,数据会变得更加稳定,之前取值范围不定的数据经过分箱后,变成了取值固定的数据。这样经过分箱后,特征包容异常值的能力增强,特征出现模型无法处理值的可能性也就降低了很多。举例来说,如果突然出现了一个1000岁的人在年龄这个特征中的话,如果没分箱,模型就炸了;但分箱后,只会把它放到“60岁以上”这个分类中,不会对模型有影响。所以模型的稳定性与鲁棒性也就提高了。
同时,在实际的建模过程中,常有一些连续型变量对结果的影响效果非常大,比如客户的资产极大的影响了客户在未来是否会对产品进行购买。这时候,这种特征会影响其他特征的表现,让模型无法将注意力放在其他特征中。因此,通过分箱,可以使特征提供的信息变得模糊,这样特征的精准度降低,模型的泛化能力增强,过拟合可能性也随之降低。
除了上面这两个比较大的好处,分箱另一个优势是会加快模型的训练速度,对数据进行离散化后,模型的复杂性降低了,计算速度会比之前快,并能够加快模型的训练速度。所以对使用数据集规模大、对模型运行效率或响应速度有要求的以及需要部署上线的模型比较合适。
此外,分箱的实际操作中,一般会将空值、缺失值单独处理,将他们在分箱操作时作为一个特殊类别存在,这样既能很好的处理空值,又能保证其存在被模型训练考虑在内。
最后,特别说一下,分箱的过程在很多分类问题上都会进行,但应用最广泛的是逻辑回归算法。这是因为在逻辑回归模型中,这主要是因为逻辑回归属于广义线性模型,它与树模型的建模原理本质是不同的。因此把一个特征离散为N个哑变量(Dummy Variable)之后,每一个哑变量就会产生一个单独的权重,相当于为模型引入非线性,进而提升模型表达能力,加大模型拟合能力。
总结一下前面的内容,分箱的好处有下面5点:
-
提高模型的稳定性与鲁棒性
-
防止过拟合问题
-
加快模型训练速度
-
很好的处理空值与缺失值
-
增强逻辑回归的拟合力
分箱方法与Python实现
可以看到,在进行模型的训练前,进行分箱是十分有必要的。但是,要想做好分箱,关键要对每个箱的边界进行定义。这里的边界就是指上面年龄例子中的18、40、60这几个关键节点。
好的边界划分既能够保证足够的数据信息传达到模型,又能够很好的包容异常值与空值;而不合理的边界切分,则反而会让特征失去原本的信息表达,或者与分箱前没有变化。因此,选择合适的分箱方法与数据切分边界是至关重要的。
下图是数据分箱的方法:
分类型特征的分箱
对于原本已经是离散化的特征,分箱的方式相对单一,我在实际中会根据两个指标评估离散化特征:
-
特征可取值的多少
-
特征是否有序
如果离散化特征可取的值非常多,如垃圾名称这种特征,它可取的值有无数种而且取值之间没有次序之分,那么可以将这种特征进行归类,合并相似项,以减少可取值的数量,比如把垃圾名称特征分箱为有害垃圾、可回收、干垃圾、湿垃圾等。
其次,如果一个特征的取值是有顺序的,比如客户等级,它的可取值为:黑金客户、白金客户、金卡客户、银卡客户、普通客户。这时候,一种分箱方法就是按照数据的等级,将文本数据转化为数值型的数据,这时候,就可以依据连续型数据的分箱方法,继续对数据进行操作的。
当然,如果数据可取的值很少,一般取值10个值以内,那我基本都不会对这种分类型特征继续进行分箱处理。这个也是根据实际数据表现而定的。
连续型特征的分箱
连续型特征的分箱分为无监督分箱与有监督分箱两类,分别各有2种应用的比较广的方法:
-
无监督分箱:不需要提供Y,仅凭借特征就能实现分箱
-
等宽分箱
-
等频分箱
-
-
有监督分箱:需要结合Y的值,通过算法实现分箱
-
决策树分箱
-
卡方分箱
-
无监督分箱
无监督分箱是比较好理解的,等距分箱与等频分箱都是不考虑好坏数据的比例以及数据分布的,这两种方法都是无脑通过数据最大值、最小值以及不同区间数据的个数直接等分。
等宽分箱也叫等距分箱,它将数据从最小值到最大值之间,平均分为 N 等份。比如前面说的年龄数据,最大值为75,最小值为3,如果试图将数据分为4份,这时候每个区间的长度就是:(75-3)/4 = 18。切分后的边界就是:21,39,47,75(amin + W, amin + 2W, …, amin +(N-1)*W)
而相对的,等频分箱是将数据的值由大到小排列后,将数据分成N等份,保证每份中数据的个数是一样的。同样是年龄这个例子,我们首先将数据从大到小排列,这个特征一共有12个数据值,我们依然希望把数据分为4个箱,这时候等频分箱中每个箱就有3个数据。
无监督分箱的Python实现方法与结果:
实现包:pandas
核心公式:
-
等距分箱:pd.cut()
-
等频分箱:pd.qcut()
代码演示:
import pandas as pd
# 导入一列数据
df = pd.DataFrame({'年龄': [29,7,49,12,50,34,36,75,61,20,3,11]})
df['等距分箱'] = pd.cut(df['年龄'] ,4) # 实现等距分箱,分为4个箱
df['等频分箱'] = pd.qcut(df['年龄'],4) # 实现等频分箱,分为4个箱
df
代码演示结果:
有监督分箱
有监督分箱相比无监督分箱要复杂一些,主要是因为比起无脑切,有监督分箱会应用卡方检验与决策树算法来确定边界。当然,这种方法虽然复杂,但是实际分箱的效果一般会更好,也更加合理。
这里要特别备注一句,之所以这两种算法叫有监督分箱,就是因为分箱边界的切分是需要Y的值才能进行的。
那这里我们先说决策树分箱,它的原理其实就是用要进行分箱的这个特征与Y进行决策树的拟合,决策树训练后的结果会提供内部节点的阈值,这个阈值就会成为分箱的边界。
简单点讲,我们如果想对年龄这组数据进行决策树分箱,我们首先要拿到预测目标Y的值。那为了实现,我们假设之前年龄数据对应的Y值如下:
年龄(原始数据) |
Y |
29 |
0 |
7 |
0 |
49 |
1 |
12 |
1 |
50 |
0 |
34 |
1 |
36 |
0 |
75 |
1 |
61 |
1 |
20 |
0 |
3 |
0 |
11 |
0 |
那么决策树分箱的方法,就是通过这两列数据训练决策树模型,并将结果中树的分支点作为分箱的边界。
一般来说,我们可以直接通过Python中的sklearn拟合决策树,并进行分箱结果提取。但是在Github中,有一个很好用的分箱包scorecardpy,可以直接实现决策树分箱。
示例如下,如图所示,我们不需要输入箱的个数,决策树分箱会自动计算并提供最合理的分箱逻辑。
import pandas as pd
import scorecardpy as sc
# 导入两列数据
df = pd.DataFrame({'年龄': [29,7,49,12,50,34,36,75,61,20,3,11],
'Y' : [0,0,1,1,0,1,0,1,1,0,0,0]})
bins = sc.woebin(df, y='Y', method='tree') # 决策树分箱
sc.woebin_plot(bins)
然后,再说卡方分箱,这种分箱方法是以卡方检验为基础衍生出来的,卡方检验原本是测试数据是否符合卡方分布的。在这里其实就是对两个相邻的区间进行卡方检验,检测它们是否存在分布上的差异。通过卡方分箱得到的箱数是通过预先设定的阈值决定,这个方法有点类似合并同类项,算法会将具有最小卡方值的区间与相邻的最小卡方区间进行合并,合并到分箱个数达到阈值为止。
在Python中,之前提到的scorecardpy包同样可以实现卡方分箱。
import pandas as pd
import scorecardpy as sc
# 导入两列数据
df = pd.DataFrame({'年龄': [29,7,49,12,50,34,36,75,61,20,3,11],
'Y' : [0,0,1,1,0,1,0,1,1,0,0,0]})
bins = sc.woebin(df, y='Y', method='chimerge') # 卡方分箱
sc.woebin_plot(bins)
分箱与算法应用
最后,我们说一下应用分箱技术的算法与场景。
分箱操作一般只在分类问题中进行,在分类问题中,对逻辑回归算法,分箱是极为重要且必须的。而对于树模型,如lightGBM、XGBoost等模型,分箱不是一个必须操作,但是却能够预防模型的过拟合并使模型的稳定性更好。
就是分类问题中的逻辑回归(LR,Logistic Regression)算法。之所以它必须用分箱,原因是逻辑回归的本质是线性模型,如果不进行分箱,不只特征的鲁棒性会极差,而且会导致模型的倾斜,很可能无法产生很好的结果。
而树模型(lightGBM、XGBoost等)对所有的输入特征都当做数值型对待,如果是离散型的数据,是需要进行One-Hot编码的,即将特征转化为哑变量(Dummy Variables)。因此分箱不是必须的,但是分箱仍然可以帮助模型预防过拟合。需要注意的只是,分箱后,还是需要把数据进行哑变量处理的。