很多刚开始建模的同学,对原始变量转WOE都是一知半解,弄不清楚为什么要转WOE,也不清楚要怎么把变量转成WOE。
对于WOE原理不清楚的小伙伴,可以先看下本公众号之前的文章:风控建模中的IV和WOE。
本文重点讲解用Python中的toad库实现变量的WOE转换。
一、WOE的定义
WOE(Weight of Evidence)叫做证据权重。可以写成两种形式,分别对应了两种不同的解释。
第一种:
WOEi = ln(第i个分箱的坏样本数/总坏样本数)-ln(第i个分箱的好样本数/总好样本数)
此时可以理解为:对于第i个分箱,该箱中坏样本在总坏样本中的占比和该箱中好样本在总好样本中占比的差异性。
或者说坏样本分布和好样本分布之间的差异性。
我们来看一种极端的情况,看看WOE的值会是多少。
表 1 - 极端例子1(用第一种方法算WOE)
从表1知,如果该箱中坏样本在总坏样本中的占比和该箱中好样本在总好样本中占比相同,WOEi为0。
或者说坏样本分布和好样本分布之间完全相同,那么所有的WOEi都为0。想一想这有什么启发?
第二种:
WOEi = ln(第i个分箱的坏样本数/第i个分箱的好样本数)-ln(总坏样本数/总好样本数)
此时可以理解为:每个分箱的坏好比和总体坏好比之间的差异。
表 2 - 极端例子1(用第二种方法算WOE)
从表2知,如果每个分箱中的坏好比和总体坏好比相同(无差异),则WOEi为0,且两种形式的公式计算结果相同。
可以发现WOE就是计算第i个分箱中,坏样本分布和好样本分布之间的差异,或者该箱中坏好比和总体坏好比的差异。
代码语言:javascript复制二、Python实现变量WOE转换
1 读取数据
首先导入挑选完入模变量后的建模数据,包括12个自变量,1个因变量。
具体代码如下:
import os
import pandas as pd
os.chdir(r'E:\date')
train_f = pd.read_csv('train_date_f.csv', encoding='gbk')
test_f = pd.read_csv('test_date_f.csv', encoding='gbk')
train_f = train_f.drop(columns='Unnamed: 0')
test_f = test_f.drop(columns='Unnamed: 0')
train_f.head(10)
得到结果:可以发现此时的自变量还是原始数据。
2 变量批量分箱在做变量的WOE变换之前需要先做变量分箱,分箱的好坏直接影响WOE的结果,以及变换后的单调性。toad支持等频分箱、等距分箱、卡方分箱、决策树分箱、最优分箱等。
并且,toad的分箱功能支持数值型数据和离散型数据。
toad首先判断变量类型,如果为数值型就按数值型分箱处理,如果为非数值型,那么会判断变量唯一值的个数,如果大于10个或者超过变量总数的50%,那么也按照数值型处理。
具体代码如下:
import toad
c = toad.transform.Combiner()
#初始化
c.fit(train_f, y = 'target', method = 'chi', min_samples = 0.05)
#使用特征筛选后的数据进行训练,使用稳定的卡方分箱,规定每箱至少有5%数据,空值我已经转换成-999999
c.export()
参数详解:
train_f:包含自变量和因变量的数据。
y:因变量列名。
method:分箱方法,包括chi(卡方), dt(决策树), kmean(k均值), quantile(等频), step(等距),默认chi。
min_samples: 每箱至少包含样本量,可以是数字或者占比。
c.export:查看分箱节点。
c.fit中还有两个参数,可根据需要进行设置,补充如下:
n_bins: 箱数,若无法分出这么多箱,则会分出最多的箱数。
empty_separate: 是否将空值单独分箱,默认是False,即将空值自动归到最佳箱。
得到结果:
可以发现r360_score变量的分割点是490,DXM_score变量的分割点分别是388、 417和 437。
3 绘制单变量分箱结果为了更清晰地看到单变量分箱结果,我们把它绘制到图形上,代码如下:
from toad.plot import bin_plot
col = 'DXM_score'
bin_plot(c.transform(train_f[[col,'target']], labels=True), x=col, target='target')
得到结果:
可以发现和上节中DXM_score变量的分割点是一致的。
4 调整分箱卡方分箱的结果可能不满足单调性,或者不符合业务逻辑等,我们可以根据经验手动调整分箱。以txy_score变量为例,先看下原始变量的分箱结果,代码如下:
col = 'txy_score'
bin_plot(c.transform(train_f[[col,'target']], labels=True), x=col, target='target')
得到结果:
手动调整分箱,代码如下:
#调整分箱
col = 'txy_score'
rule = {col:[0, 50, 55, 65, 74, 80]}
c.update(rule)
bin_plot(c.transform(train_f[[col, 'target']], labels=True), x=col, target='target')
得到结果:
一般手动调节的目的是考虑特殊值、单调性、分箱的数量是否合理等,本文的例子旨在提供实现的代码。
5 变量转WOE
最后是实现变量原始值转woe,具体如下代码:
transer = toad.transform.WOETransformer()
# 初始化
train_woe = transer.fit_transform(c.transform(train_f), train_f['target'], exclude=['target'])
#转化训练数据,并去掉target列
test_woe = transer.transform(c.transform(test_f))
#转化测试数据
train_woe.head(10)
得到结果:
跟原始数据对比,可以发现每个变量都做了变换。
从本章的第二小节可知,r360_score变量的分割点是490,说明这个变量分成了两箱。
我们看下WOE转换后的值是否为2个,代码如下:
train_woe['r360_score'].value_counts()
得到结果:
0.193863 9483
-0.291006 7316
Name: r360_score, dtype: int64
可以发现其中一箱的WOE值是0.193863,样本个数为9483,另一箱的WOE值是-0.291006,样本个数为7316。
刚刚我们也对txy_score的变量进行手动分箱,由原来卡方分箱的5箱,变成了手动分箱的7箱。
我们验证下,手动分箱的结果有没有保存,代码如下:
train_woe['txy_score'].value_counts()
得到结果:
-0.703812 5760
-0.032115 3427
0.322981 3288
0.669115 1823
-0.231586 1438
0.786913 1055
1.386220 8
Name: txy_score, dtype: int64
从结果知,转WOE时有7个值,即保留了手动分箱的结果。
至此,风控建模中把原始变量转成WOE实现已讲解完毕
往期回顾:
一文囊括Python中的函数,持续更新。。。
一文囊括Python中的有趣案例,持续更新。。。
一文囊括Python中的数据分析与绘图,持续更新。。。
一文囊括风控模型搭建(原理+Python实现),持续更新。。。