特征工程之特征分箱:决策树分箱、卡方分箱、bestks以及评价标准

  • 1.WOE和IV
  • 2.无监督分箱
    • 2.1等频分箱
    • 2.2等距分箱
  • 3.有监督分箱
    • 3.1决策树分箱
    • 3.2best-ks分箱
    • 3.3卡方分箱

前言:在做数据挖掘项目的时候,特征工程通常是其中非常重要的一个环节,但是难度也比较高,并且不同项目特征工程会有所差异,因此在做相关项目时可以多总结一些对结果提升比较明显的操作,近期做了一下天池上面的入门金融风控的项目,发现在对部分连续变量和离散变量进行分箱之后和woe编码后效果有一定提升。

废话不多说进入正题:在特征工程我们通常可以对连续值属性进行分箱操作(也就是常说的离散化),并且对于取值较多的离散变量也可进行分箱操作,分箱之后主要有以下好处:

  • 分箱后的特征对异常数据有更好的鲁棒性。例如年龄中含有200,300这类的数据,分箱之后就可能划到>80这一箱中,但是如果直接传入模型训练的话会对模型造成很大干扰。
  • 可以将变量转换到相似的尺度上。例如收入有1000,10000,百万,千万等,可以离散化为0(低收入),1(中等收入),2(高收入)等。
  • 离散后的变量方便我们做特征交叉。例如将离散后的特征与连续变量进行groupby,构造均值、方差等。
  • 缺失值的处理,可以将缺失值合并为一个箱。

并且分箱我们通常会遵循以下原则:

  • 组内差异小
  • 组间差异大
  • 每组占比不小于5%
  • 必须有好坏两种分类(对于二分类而言)

对于某个属性分箱过后的结果是好还是坏,我们可以使用WOE和IV进行评估。

1.WOE和IV

(1)WOE(Weight Of Evidence),即证据权重,其表达式如下:
W O E i = l n ( g o o d 占 比 / b a d 占 比 ) = l n ( g o o d i / g o o d T b a d i / b a d T ) WOE_i=ln(good占比/bad占比)\\=ln(\frac{good_i/good_T}{bad_i/bad_T}) WOEi​=ln(good占比/bad占比)=ln(badi​/badT​goodi​/goodT​​)
其中, W O E I WOE_I WOEI​表示第i组的证据权重, g o o d i good_i goodi​表示第i组中标签为good的数, g o o d T good_T goodT​表示数据集中标签为good的总的数量, b a d bad bad同理。
(2)IV(Information Value),是用来衡量某一变量的信息量。其计算方式如下:
I V = ∑ i = 1 N ( g o o d 占 比 − b a d 占 比 ) ∗ W O E i IV=\sum_{i=1}^{N}(good占比-bad占比)*WOE_i IV=i=1∑N​(good占比−bad占比)∗WOEi​
其中N是分箱的箱数,IV可以用来衡量变量的预测能力。也就是当我们的分箱结果IV值小于0.03就可以不考虑的分箱

woe和iv的python实现:

def WOE(data, feat, label):bin_values = data[feat].unique()good_total_num = len(data[data[label]==1])bad_total_num = len(data[data[label]==0])woe_dic = {}df = pd.DataFrame()for i,val in enumerate(bin_values):good_num = len(data[(data[feat]==val) & (data[label]==1)])bad_num = len(data[(data[feat]==val) & (data[label]==0)])df.loc[i,feat] = valdf.loc[i, feat+'_woe'] = np.log( (good_num/good_total_num) / ((bad_num/bad_total_num+0.0001)) )woe_dic[val] = np.log( (good_num/good_total_num) / ((bad_num/bad_total_num+0.0001)) )return woe_dic,dfdef IV(data, woe_dic, feat, label):good_total_num = len(data[data[label] == 1])bad_total_num = len(data[data[label] == 0])bin_values = data[feat].unique()feat_IV = 0for val in bin_values:woe = woe_dic[val]good_num = len(data[(data[feat] == val) & (data[label] == 1)])bad_num = len(data[(data[feat] == val) & (data[label] == 0)])feat_IV += ((good_num/good_total_num)-(bad_num/bad_total_num))*woereturn feat_IV

知道了特征分箱后的评估方法之后就可以考虑如何进行分箱。而分箱的方法主要分为两大类:无监督分箱(等频分箱、等距分箱),有监督分箱(best-ks分箱、决策树分箱、卡方分箱)。

2.无监督分箱

2.1等频分箱

等频分箱的意思就是分箱之后,每个箱内的数据量相等。可以用pandas提供的qcut方法进行处理。

2.2等距分箱

等距分箱就是每个箱的区间的大小是相等的,每个箱内的数据量不一定相等。

例子:

import pandas as pd
import numpy as npdata = np.random.randn(100)
#等频分箱
data_bins = pd.qcut(data, q=5)
print(data_bins.value_counts())
#等距分箱
data_bins = pd.cut(data,bins=5)
print(data_bins.value_counts())

输出结果:通过结果我们也能很明显看出两种分箱的差异

(-2.554, -0.844]    20
(-0.844, -0.153]    20
(-0.153, 0.25]      20
(0.25, 1.049]       20
(1.049, 2.385]      20
dtype: int64
(-2.558, -1.566]     6
(-1.566, -0.578]    21
(-0.578, 0.41]      43
(0.41, 1.397]       21
(1.397, 2.385]       9
dtype: int64

3.有监督分箱

3.1决策树分箱

决策树分箱的原理就是用想要离散化的变量单变量用树模型拟合目标变量,例如直接使用sklearn提供的决策树(是用cart决策树实现的),然后将内部节点的阈值作为分箱的切点。

补充,cart决策树和ID3、C4.5决策树不同,cart决策树对于离散变量的处理其实和 连续变量一样,都是将特征的所有取值从小到大排序,然后取两两之间的均值,然后遍历所有这些均值,然后取gini系数最小的点作为阈值进行划分数据集。并且该特征后续还可参与划分。
这里需要说明一下:cart的决策树是一颗二叉树,所以对于离散变量处理时会遍历所有值,然后取一个作为一类,剩下的作为另一类,这样建树的结果就是一颗二叉树;但在sklearn包并没有实现对离散属性的单独处理,所以我们传入的离散属性值也会被当成连续值去处理。
“scikit-learn uses an optimised version of the CART algorithm; however, scikit-learn implementation does not support categorical variables for now.”

python实现:

from sklearn.tree import DecisionTreeClassifier
def optimal_binning_boundary(x, y):'''利用决策树获得最优分箱的边界值列表,利用决策树生成的内部划分节点的阈值,作为分箱的边界'''boundary = []  # 待return的分箱边界值列表x = x.fillna(-1).values  # 填充缺失值y = y.valuesclf = DecisionTreeClassifier(criterion='entropy',  # “信息熵”最小化准则划分max_leaf_nodes=6,  # 最大叶子节点数min_samples_leaf=0.05)  # 叶子节点样本数量最小占比clf.fit(x, y)  # 训练决策树#tree.plot_tree(clf) #打印决策树的结构图#plt.show()n_nodes = clf.tree_.node_count #决策树的节点数children_left = clf.tree_.children_left #node_count大小的数组,children_left[i]表示第i个节点的左子节点children_right = clf.tree_.children_right #node_count大小的数组,children_right[i]表示第i个节点的右子节点threshold = clf.tree_.threshold #node_count大小的数组,threshold[i]表示第i个节点划分数据集的阈值for i in range(n_nodes):if children_left[i] != children_right[i]:  # 非叶节点boundary.append(threshold[i])boundary.sort()min_x = x.min()max_x = x.max() + 0.1  # +0.1是为了考虑后续groupby操作时,能包含特征最大值的样本boundary = [min_x] + boundary + [max_x]return boundary

3.2best-ks分箱

KS(Kolmogorov Smirnov)用于模型风险区分能力进行评估,指标衡量的是好坏样本累计部分之间的差距 。KS值越大,表示该变量越能将正,负客户的区分程度越大。通常来说,KS>0.2即表示特征有较好的准确率。

KS的计算方式:计算分箱后每组的好账户占比和坏帐户占比的差值的绝对值: ∣ g o o d 占 比 − b a d 占 比 ∣ |good占比-bad占比| ∣good占比−bad占比∣,然后取所有组中最大的KS值作为变量的KS值。

best-ks分箱的原理:

  1. 将变量的所有取值从小到大排序
  2. 计算每一点的KS值
  3. 选取最大的KS值对应的特征值 x m x_m xm​,将特征x分为 x i ≤ x m {x_i\leq x_m} xi​≤xm​和 x i > x m {x_i>x_m} xi​>xm​两部分。然后对于每部分重复2-3步。

python代码实现:

def best_ks_box(data, var_name, box_num):data = data[[var_name, 'isDefault']]"""KS值函数"""def ks_bin(data_, limit):g = data_.iloc[:, 1].value_counts()[0]b = data_.iloc[:, 1].value_counts()[1]data_cro = pd.crosstab(data_.iloc[:, 0], data_.iloc[:, 1])data_cro[0] = data_cro[0] / gdata_cro[1] = data_cro[1] / bdata_cro_cum = data_cro.cumsum()ks_list = abs(data_cro_cum[1] - data_cro_cum[0])ks_list_index = ks_list.nlargest(len(ks_list)).index.tolist()for i in ks_list_index:data_1 = data_[data_.iloc[:, 0] <= i]data_2 = data_[data_.iloc[:, 0] > i]if len(data_1) >= limit and len(data_2) >= limit:breakreturn i"""区间选取函数"""def ks_zone(data_, list_):list_zone = list()list_.sort()n = 0for val in list_:m = sum(data_.iloc[:, 0] <= val) - nn = sum(data_.iloc[:, 0] <= val)print(val,' , m:',m,' n:',n)list_zone.append(m)#list_zone[i]存放的是list_[i]-list[i-1]之间的数据量的大小list_zone.append(50000 - sum(list_zone))print('sum ',sum(list_zone[:-1]))print('list zone ',list_zone)#选取最大数据量的区间max_index = list_zone.index(max(list_zone))if max_index == 0:rst = [data_.iloc[:, 0].unique().min(), list_[0]]elif max_index == len(list_):rst = [list_[-1], data_.iloc[:, 0].unique().max()]else:rst = [list_[max_index - 1], list_[max_index]]return rstdata_ = data.copy()limit_ = data.shape[0] / 20  # 总体的5%""""循环体"""zone = list()for i in range(box_num - 1):#找出ks值最大的点作为切点,进行分箱ks_ = ks_bin(data_, limit_)zone.append(ks_)new_zone = ks_zone(data, zone)data_ = data[(data.iloc[:, 0] > new_zone[0]) & (data.iloc[:, 0] <= new_zone[1])]zone.append(data.iloc[:, 0].unique().max())zone.append(data.iloc[:, 0].unique().min())zone.sort()return zone

3.3卡方分箱

卡方检验的原理:卡方分箱是依赖于卡方检验的分箱方法,在统计指标上选择卡方统计量(chi-Square)进行判别,分箱的基本思想是判断相邻的两个区间是否有分布差异,基于卡方统计量的结果进行自下而上的合并,直到满足分箱的限制条件为止。

基本步骤:

  1. 预先设定一个卡方的阈值
  2. 对需要进行离散的属性进行排序,每个取值属于一个区间
  3. 合并区间:(1)计算每一对相邻区间的卡方值: X 2 = ∑ i = 1 2 ∑ j = 1 2 ( A i j − E i j ) 2 E i j X^2=\sum_{i=1}^2\sum_{j=1}^{2}\frac{(A_{ij}-E_{ij})^2}{E_{ij}} X2=∑i=12​∑j=12​Eij​(Aij​−Eij​)2​;(2)然后将卡方值最小的一对区间合并。
    其中 A i j A_{ij} Aij​是第i区间第j类的样本数量, E i j E_{ij} Eij​是 A i j A_{ij} Aij​的期望数量 = N i ∗ C j N =N_i*\frac{C_j}{N} =Ni​∗NCj​​,N是总样本数,N_i是第i组的样本数,C_j是第j类样本数量。

上述步骤的终止条件:

  1. 分箱个数:每次将样本中具有最小卡方值的区间与相邻的最小卡方区间进行合并,直到分箱个数达到限制条件为止。
  2. 卡方阈值:根据自由度和显著性水平得到对应的卡方阈值,如果分箱的各区间最小卡方值小于卡方阈值,则继续合并,直到最小卡方值超过设定阈值为止。

python代码实现:

# 计算2*2列联表的卡方值
def get_chi2_value(arr):rowsum = arr.sum(axis=1)  # 对行求和,大小为M的数组,是当前分箱情况下,每个箱的样本数量colsum = arr.sum(axis=0)  # 对列求和,大小为N_class的数组,每个元素就是每个类别的样本数量n = arr.sum()emat = np.array([i * j / n for i in rowsum for j in colsum]) #计算每个区间下每个类别样本数量的期望值,大小为M*N_classarr_flat = arr.reshape(-1)arr_flat = arr_flat[emat != 0]  # 剔除了期望为0的值,不参与求和计算,不然没法做除法!emat = emat[emat != 0]  # 剔除了期望为0的值,不参与求和计算,不然没法做除法!E = (arr_flat - emat) ** 2 / ematreturn E.sum()# 自由度以及分位点对应的卡方临界值
def get_chi2_threshold(percents, nfree):return chi2.isf(percents, df=nfree)# 计算卡方切分的切分点
def get_chimerge_cutoff(ser, tag, max_groups=None, threshold=None):freq_tab = pd.crosstab(ser, tag)cutoffs = freq_tab.index.values  # 保存每个分箱的下标freq = freq_tab.values  # [M,N_class]大小的矩阵,M是初始箱体的个数,N_class是目标变量类别的个数while True:min_value = None #存放所有对相邻区间中卡方值最小的区间的卡方值min_idx = None #存放最小卡方值的一对区间中第一个区间的下标for i in range(len(freq) - 1):chi_value = get_chi2_value(freq[i:(i + 2)]) #计算第i个区间和第i+1个区间的卡方值if min_value == None or min_value > chi_value:min_value = chi_valuemin_idx = iif (max_groups is not None and max_groups < len(freq)) or (threshold is not None and min_value < get_chi2_threshold(threshold, len(cutoffs)-1)):tmp = freq[min_idx] + freq[min_idx + 1] #合并卡方值最小的那一对区间freq[min_idx] = tmpfreq = np.delete(freq, min_idx + 1, 0) #删除被合并的区间cutoffs = np.delete(cutoffs, min_idx + 1, 0)else:breakreturn cutoffs

参考博客:
python实现卡方分箱Chi-Merge
特征工程之分箱–Best-KS分箱
风控场景下的常用特征分箱介绍:BestKs分箱、卡方分箱、聚类分箱等
【评分卡】评分卡入门与创建原则——分箱、WOE、IV、分值分配

特征工程之特征分箱(决策树分箱、卡方分箱、bestks以及评价标准WOE和IV)相关推荐

  1. python 卡方分箱算法_特征工程 - 分箱

    卡方分箱 python自帶分箱函數  --  無法實現對分類型數據的分箱,可借助卡方分箱算法實現 import numpy as np import pandas as pd data = np.ra ...

  2. python评分卡建模-卡方分箱

    今天主要给大家讲讲卡方分箱算法ChiMerge.先给大家介绍一下经常被提到的卡方分布和卡方检验是什么. 一.卡方分布 卡方分布(chi-square distribution, χ2-distribu ...

  3. 卡方 python_Python评分卡建模—卡方分箱

    原标题:Python评分卡建模-卡方分箱 今天主要给大家讲讲卡方分箱算法ChiMerge.先给大家介绍一下经常被提到的卡方分布和卡方检验是什么. 一.卡方分布 卡方分布(chi-square dist ...

  4. python卡方分箱_Python评分卡建模—卡方分箱

    为服从自由度为k的卡方分布,记作: 或者记作 . 二.卡方检验 χ2检验是以χ2分布为基础的一种假设检验方法,主要用于分类变量之间的独立性检验. 其基本思想是根据样本数据推断总体的分布与期望分布是否有 ...

  5. 金融风控--申请评分卡模型--特征工程(特征分箱,WOE编码) 标签: 金融特征分箱-WOE编码 2017-07-16 21:26 4086人阅读 评论(2) 收藏 举报 分类: 金融风

    金融风控-->申请评分卡模型-->特征工程(特征分箱,WOE编码) 标签: 金融特征分箱-WOE编码 2017-07-16 21:26 4086人阅读 评论(2) 收藏 举报 分类: 金融 ...

  6. 金融风控-- >申请评分卡模型-- >特征工程(特征分箱,WOE编码)

    这篇博文主要讲在申请评分卡模型中常用的一些特征工程方法,申请评分卡模型最多的还是logsitic模型. 先看数据,我们现在有三张表: 已加工成型的信息: Master表 idx:每一笔贷款的uniqu ...

  7. 【特征工程】特征分箱

      对数据分析.机器学习.数据科学.金融风控等感兴趣的小伙伴,需要数据集.代码.行业报告等各类学习资料,可添加微信:wu805686220(记得要备注喔!),也可关注微信公众号:风控圏子(别打错字,是 ...

  8. python卡方分箱_机器学习(十六)特征工程之数据分箱

    1 分箱简介 数据分箱(也称为离散分箱或分段)是一种数据预处理技术,用于减少次要观察误差的影响,是一种将多个连续值分组为较少数量的"分箱"的方法. 例如,例如我们有一组关于人年龄的 ...

  9. 特征离散化(一) 之 卡方分箱

    特征离散化(一) 之 卡方分箱 特征离散化(二) 之 Chi2分箱 特征离散化(三) 之 最小熵分箱 特征离散化(四) 之 bestKS分箱 特征离散化(五) 之 评分卡最优分箱 离散特征在数据挖掘的 ...

最新文章

  1. 性能测试,负载测试,压力测试以及容量测试的联系与区别--网搜及总结
  2. Python大佬 | 菜鸟进阶必备的九大技能!
  3. ​2022年应用人工智能会是什么样子?
  4. You should rebuild using libgmp = 5 to avoid timing attack vulnerability
  5. 【Spring学习】IoC、DI、AOP入门学习
  6. Linux内核:容器底层cgroup如何使用
  7. APScheduler Scheduler
  8. java表白程序_Java实现表白小程序
  9. Java复习 day_01
  10. 苹果电脑怎么自定义音频输出设备
  11. __mian__的作用
  12. Java 基础篇:第十九章:多线程
  13. java 中的todo_详解在TodoController中引用TodoRepository
  14. 彼得德鲁克管理理念摘写
  15. Android 11 新特性和API兼容
  16. 如何在windows上使用VMware安装macOS虚拟机
  17. 做前端,如何克服新技术焦虑?
  18. 紫书 习题 11-12 UVa 1665 (并查集维护联通分量)
  19. Android学之客户端服务端Socket简单通信
  20. “富贵树”身价十五万,湖南小伙返乡创业,一年卖出800多万

热门文章

  1. 苏州地区--校招IT公司
  2. 如何安装操作系统?过程、图文。
  3. 计算机科学——计算机导论
  4. flashback table 闪回表到指定时间或SCN
  5. Windows10鼠标右键增加新项
  6. oracle查询同一天生日的,数据分析经典问题:两个朋友同一天过生日的概率?
  7. Python爬虫圈最能打的专栏教程,《Python爬虫120例》教程导航帖(2023.2.6更新)
  8. 分析肖特基二极管的优势与结构应用
  9. 64匹马,8个赛道,通过比赛,决出前四名,最少需要比赛次数
  10. Docker之Docker概述