前言
本篇承接上篇
分箱逻辑比较复杂 设计到很多的算法
为了确保分析的准确性 我通过pycharm编译器Debug的方式跑这段代码
一步一步的分析代码的实现逻辑
编译器环境的准备
python代码准备
![](https://imgkr2.cn-bj.ufileos.com/59301e9c-10d6-4ad8-96ba-05a6d6f34344.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=6YUe6moCGSC7IvljThTmoJmm2Vs%253D&Expires=1607482554)
编译器配置
![](https://imgkr2.cn-bj.ufileos.com/de9e2e0a-1a00-4322-9076-df0478b066e7.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=RKf6nkYk52Wwfrv0mBQKu7Nfuvk%253D&Expires=1607482572)
![](https://imgkr2.cn-bj.ufileos.com/2f113323-2dcb-42ba-a009-886c5d2958cd.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=%252FuDfWfGzM5N9M3eoAYl8wYtVbmM%253D&Expires=1607482602)
![](https://imgkr2.cn-bj.ufileos.com/8c97a201-c822-4b45-9198-64791374acdd.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=6u7Myg7NxXNyb63jyxAuu37wqFk%253D&Expires=1607482615)
![](https://imgkr2.cn-bj.ufileos.com/c85534e4-ae91-464c-a4d4-7aab6ec698d4.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=WJZ7jdESucXrrhc6EO0r%252BUxUzGM%253D&Expires=1607482631)
![](https://imgkr2.cn-bj.ufileos.com/2393b6fe-cdd3-4ec1-8b40-73212f3a4c26.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=AAMPTQTyRdgDinKSP5EqS4CELSI%253D&Expires=1607482648)
![](https://imgkr2.cn-bj.ufileos.com/89995bfd-eed0-47e5-b5b2-345c8584c982.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=KHvRSx8kxKGRDb%252FEEdPy%252FZNZkpw%253D&Expires=1607482662)
![](https://imgkr2.cn-bj.ufileos.com/081b37cc-8024-4844-98c2-a18ed9947e38.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=z0LSIihvflBANlSTWc4pE5jrbWE%253D&Expires=1607482675)
python依赖包安装
![](https://imgkr2.cn-bj.ufileos.com/ba14b749-8639-46f2-8aa4-b9be6c4239bc.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=dKq%252Fjv4Yv6SCpJ8bSKdw25u8bB8%253D&Expires=1607482692)
pip3 install numpy
pip3 install pandas
pip3 install matplotlib==3.2.0
pip3 install --target=./venv/lib/python3.7/site-packages seaborn
pip3 install ipython
pip3 install xlrd
pip3 install sklearn
代码逻辑分析
自动分箱
# 调用自定义分箱
dfx1, ivx1, cutx1, woex1 = mono_bin(train.SeriousDlqin2yrs, train.RevolvingUtilizationOfUnsecuredLines, n=10)
# 自定义自动分箱函数
def mono_bin(Y, X, n=20):
r = 0
# 好人个数 6936
good = Y.sum()
# 坏人个数 94957
bad = Y.count() - good
# np.abs(0)=0.9272727272727272m
absR = np.abs(r)
while absR < 1: # 满足循环条件 进入循环体
# 先对训练集X排序 再分组
dfX = X.rank(method="first")
cutPd = pd.qcut(dfX, n)
d1 = pd.DataFrame({"X": X, "Y": Y, "Bucket": cutPd}) # X.rank(method="first")
d2 = d1.groupby("Bucket", as_index = True)
d2Mean = d2.mean()
d2MeanX = d2Mean.X
r, p = stats.spearmanr(d2MeanX, d2.mean().Y) # 使用斯皮尔曼等级相关系数来评估两个变量之间的相关性
n = n - 1
d3 = pd.DataFrame(d2.X.min(), columns = ['min'])
d3['min']=d2.min().X
d3['max'] = d2.max().X
d3['sum'] = d2.sum().Y
d3['total'] = d2.count().Y
d3['rate'] = d2.mean().Y
d3['woe'] = np.log((d3['rate'] / (1-d3['rate'])) / (good/bad))
d3['goodattribute'] = d3['sum'] / good
d3['badattribute'] = (d3['total'] - d3['sum']) / bad
iv = ((d3['goodattribute'] - d3['badattribute']) * d3['woe']).sum()
d4 = (d3.sort_values(by = 'min'))
print(d4)
cut=[]
cut.append(float('-inf'))
for i in range(1, n+1):
qua = X.quantile(i/(n+1))
cut.append(round(qua,4))
cut.append(float('inf'))
woe = list(d4['woe'].round(3))
return d4, iv, cut, woe
详细分析分箱逻辑
参数分析
-
train.SeriousDlqin2yrs 对应函数中的X值
61003 0
26974 0
64962 0
22271 0
21614 0
..
133033 0
123509 0
18246 0
28850 0
16172 0
第一列参数 表示 一行索引值 可以理解成唯一主键id
第二列参数 表示 真实值 0表示坏客户 ,1 表示好客户
-
train.RevolvingUtilizationOfUnsecuredLines 对应函数中的Y值
RevolvingUtilizationOfUnsecuredLines 信用卡和个人信用额度的总余额,除了房地产和没有分期付款债务,如汽车贷款除以信用额度
61003 0.119104
26974 0.042524
64962 0.067740
22271 0.866513
21614 1.000000
...
133033 0.367954
123509 0.031879
18246 0.090163
28850 1.644518
16172 0.160312
第一列是行索引值
第二列表示总额度
函数实现逻辑分析
Y.sum() 表示好人总计
Y.count() 表示所有人
Y.count() - good 表示坏人个数
a、 循环初始条件:r = 0,n=10
b、 while循环条件:np.abs(r)<1
取绝对值
c-1、 循环过程中使用 斯皮尔曼等级相关系数 算法重新计算r值
c-2、 每次循环:n = n - 1
第一次循环: r=0
np.abs(0)==0 进入循环体
这个是对训练集X进行顺序排名 重复的值 谁出现在前面 就先排谁
X.rank(method="first")
这里简单介绍下 rank排名函数和qcut\cut函数
理解了这些函数的作用就可以更好的理解分箱算法对于数据处理的过程
-
rank函数
举一个简单的例子
有一个数据源 字段:班级、姓名、成绩
![](https://imgkr2.cn-bj.ufileos.com/327926a2-f296-4985-891d-8a0f685e897b.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=c8Pzm26BBvtRPRs0r0oZZIlDaf4%253D&Expires=1607485278)
目的:找出每个班级中排名第二的学生信息
需要做的步骤:
1、根据班级分组
2、每个组计算排名
3、筛选出排名为第二的学生
此时对于相同成绩的同学如何排名
a 顺序排名 先到先得
李四和王五的成绩都为30,但是李四出现在王五的前面,所以李四的排名靠前
当method取值为min,max,average时,都是要参考“顺序排名”的)
![](https://imgkr2.cn-bj.ufileos.com/949def3c-fbd6-4fb8-b4ef-6b7c80e84f8a.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=gjQ4UkI%252FzC4wH02UmVbWBUfGyrw%253D&Expires=1607485308)
b 密集排名:成绩相同 排名相同
相同成绩的同学排名相同,其他依次加1即可
1,2,2,3,4
![](https://imgkr2.cn-bj.ufileos.com/4e1dfc36-94d4-4f60-af3a-baba14c81247.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=RoHPPiSSQZi3GmauQmmbkBXbNX0%253D&Expires=1607485345)
c 跳跃排名:成绩相同 排名相同
1,2,2,4,5
成绩相同的同学,取在顺序排名中最小的那个排名作为该值的排名,李四和王五同学排名分别为2和3,那么当method为min时,取2和3的最小的那个作为第2名作为成绩30的排名
![](https://imgkr2.cn-bj.ufileos.com/82876d6c-9350-448e-9a6e-8d035db79fcf.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=R9ehhqkVFQyqNGsu3jMwW4vT0G0%253D&Expires=1607485328)
rank函数取值范围
‘average’,’first’,’min’, ‘max’,’dense’
min和max是跳跃排名的一种
关于average,成绩相同时,取顺序排名中所有名次之和除以该成绩的个数,即为该成绩的名次;比如上述排名中,30排名为2,3,那么 30的排名 = (2+3)/2=2.5,成绩为50的同学只有1个,且排名为1,那50的排名就位1/1=1。
关于max,和min一样也是跳跃排名的一种,成绩相同时取顺序排名中排名最大的作为该成绩的名次,在顺序排名中,30最大的排名为3,那么当参数为max时,30的排名=3,此时,李四和王五的排名都为第3名了。
-
qcut函数
跟cut()按照变量的值对变量进行分割不同, qcut()是按变量的数量来对变量进行分割,并且尽量保证每个分组里变量的个数相同
例子:把数据由小到大分成四组,并且让每组数据的数量相同
# 把变量由小到大分成四组,并且让每组变量的数量相同
d_qcut = d.copy()
d_qcut['qcut_group'] = pd.qcut(d_qcut['number'], 4)
d_qcut
![](https://imgkr2.cn-bj.ufileos.com/c4f2dbee-f62f-4f4f-b44e-4549a48406a9.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=10j9eGYGOE%252BU8udNJQvozkexnOE%253D&Expires=1607485719)
# 查看每个分组里变量的个数
d_qcut['qcut_group'].value_counts()
![](https://imgkr2.cn-bj.ufileos.com/ba169786-f1a1-447d-87f8-f38291bd5179.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=Hcwl7eGwL%252BC6QzdGqCPtDjkvX6U%253D&Expires=1607500591)
使用qcut()对数据进行分割之后,每个分组里的数据个数都大致相同,但是跟cut()不同的是,每个分组里值的范围并不相同
-
cut() 函数
按照指定的边界值对变量进行分割
# 使用bins参数, 指定每个分组的边界
d_cut_bins = d.copy()
d_cut_bins['cut_group'] = pd.cut(d_cut_bins['number'],
bins=[0, 10, 50, 100])
d_cut_bins
![](https://imgkr2.cn-bj.ufileos.com/0d42de96-d5b7-4ed1-af2e-b273af4f3ae5.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=qnG5Gqyu8d0ZCEpzyxWFk8gnVIA%253D&Expires=1607500785)
继续信用评分系统 分箱代码分析
dfX = X.rank(method="first")
![](https://imgkr2.cn-bj.ufileos.com/0b1d6720-b425-4d72-9574-6b49a5b1f076.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=17MBvXPUBFLZSSQjBWFABb5sPVE%253D&Expires=1607501187)
cutPd = pd.qcut(dfX, n)
n=10
对dfX 分10组 每组的数量大小一致
![](https://imgkr2.cn-bj.ufileos.com/92d623de-417b-439e-899b-44d27a8ab5aa.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=QgsOsfBlUZCyV7rQ4VdFBAKNIxc%253D&Expires=1607501213)
d1 = pd.DataFrame({"X": X, "Y": Y, "Bucket": cutPd})
![](https://imgkr2.cn-bj.ufileos.com/77168ce8-72f8-458b-a0a9-3c3f22751725.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=01ULvyCPWoFOBcHKZrX1joNzy0U%253D&Expires=1607501246)
d2 = d1.groupby("Bucket", as_index = True)
![](https://imgkr2.cn-bj.ufileos.com/31707f05-2883-4f0e-a7d5-13e9615fb9c7.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=NfjF2M6Yhpl6T%252FvF6UCKN84ZRvE%253D&Expires=1607501297)
d2Mean = d2.mean()
![](https://imgkr2.cn-bj.ufileos.com/15805b56-077a-4f14-b64e-0cc420f75e60.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=ngq9nxvZquD%252FOVRUCMwQAmwNF4Q%253D&Expires=1607501319)
d2MeanX = d2Mean.X
![](https://imgkr2.cn-bj.ufileos.com/ab7cbe4f-3cd8-4d70-a749-d01b8d92b1f9.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=zlCa2DICWMjQYDvABQGcAuFPsm0%253D&Expires=1607501342)
使用斯皮尔曼等级相关系数来评估两个变量之间的相关性
r, p = stats.spearmanr(d2MeanX, d2Mean.Y)
这个具体什么原理感兴趣的朋友 可以自己查阅一下资料
![](https://imgkr2.cn-bj.ufileos.com/617e4547-e385-481f-b8be-a1d9620259d9.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=gTMo7d%252Bu4%252BxJINxx%252FAd5QGq3RyM%253D&Expires=1607504242)
![](https://imgkr2.cn-bj.ufileos.com/54fa0932-9df8-4bfc-ac02-cec10cbda8dc.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=3ynbS3UfXusEVlIDkxaV%252BkITteM%253D&Expires=1607504348)
每个指标具体计算过程
min 最小值 d2.min().X
max 最大值 d2.max().X
total 总人数 d2.count().Y
sum 好人数 d2.sum().Y
rate 均值 d2.mean().Y
woe=np.log((d3['rate'] / (1-d3['rate'])) / (good/bad))
(好人均值/坏人均值)/(好人人数/坏人人数) 取对数 即WOE
goodattribute=d3['sum'] / good 每组好人数量/总的好人数
badattribute=(d3['total'] - d3['sum']) / bad 每组坏人人数/坏人总人数
iv = ((d3['goodattribute'] - d3['badattribute']) * d3['woe']).sum()
d4 = (d3.sort_values(by = 'min'))
通过min列排序
![](https://imgkr2.cn-bj.ufileos.com/4a89ed67-eb1b-4991-b033-7584a20ab222.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=0ZvEA%252B5Br%252B49zEOw6p4w05mGnj8%253D&Expires=1607506095)
该分箱函数最终得到的结果
![](https://imgkr2.cn-bj.ufileos.com/ce1dde5c-bfba-4c50-9b4f-03db806bdc2d.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=Q4Wotuh%252BvIAtAVoEMdDjY8FABcM%253D&Expires=1607504890)
ivx1: 1.0027047570109968
cutx1: [-inf, 0.0313, 0.1583, 0.5605, inf]
woex1: [-1.37, -1.212, -0.288, 1.106]