当前位置:   article > 正文

转:Python数据分箱,计算woe,iv_woe iv计算公式

woe iv计算公式

转自:https://zhuanlan.zhihu.com/p/38440477

https://www.cnblogs.com/wzdLY/p/9853209.html

https://github.com/Lucky-Bone/Discretization

https://blog.csdn.net/SkullSky/article/details/105646062

WOE:

WOE的全称是“Weight of Evidence”,即证据权重,WOE是对原始自变量的一种编码形式。要对一个变量进行WOE编码,需要首先把这个变量进行分箱。分箱后,对于第i组,WOE的计算公式如下:

yi是这个分组中响应客户(即取值为1)的数量,yT是全部样本中所有响应客户(即取值为1)的数量

ni是这个分组中未响应客户(即取值为0)的数量,nT是全部样本中所有未响应客户(即取值为0)的数量

 

IV值:

IV的全称是Information Value,用来衡量自变量的预测能力

对于分组i的IV值:

计算整个变量的IV值,n为变量分组个数:

  • 过高的IV,可能有潜在的风险
  • 特征分箱越细,IV越高

IV值(Information Value)

在机器学习的二分类问题中,IV值(Information Value)主要用来对输入变量进行编码和预测能力评估。特征变量IV值的大小即表示该变量预测能力的强弱,在面对大量变量的情况下,可计算各个变量的IV值,取IV值大于某个固定值的变量参与到模型中去,这样不仅保留了特征携带的信息量。且提高了模型效率,此外有利于给客户解释和汇报。

数据分箱的重要性及优势:

  1. 离散特征的增加和减少都很容易,易于模型的快速迭代;
  2. 稀疏向量内积乘法运算速度快,计算结果方便存储,容易扩展;
  3. 离散化后的特征对异常数据有很强的鲁棒性:比如一个特征是年龄>30是1,否则0。如果特征没有离散化,一个异常数据“年龄300岁”会给模型造成很大的干扰;
  4. 逻辑回归属于广义线性模型,表达能力受限;单变量离散化为N个后,每个变量有单独的权重,相当于为模型引入了非线性,能够提升模型表达能力,加大拟合;
  5. 离散化后可以进行特征交叉,由M+N个变量变为M*N个变量,进一步引入非线性,提升表达能力;
  6. 特征离散化后,模型会更稳定,比如如果对用户年龄离散化,20-30作为一个区间,不会因为一个用户年龄长了一岁就变成一个完全不同的人。当然处于区间相邻处的样本会刚好相反,所以怎么划分区间是门学问;
  7. 特征离散化以后,起到了简化了逻辑回归模型的作用,降低了模型过拟合的风险。
  8. 可以将缺失作为独立的一类带入模型。
  9. 将所有变量变换到相似的尺度上。
  • 无监督分箱:
  1. 等频分箱:区间的边界值要经过选择,使得每个区间包含大致相等的实例数量。比如说 N=10 ,每个区间应该包含大约10%的实例。
  1. def bin_frequency(x,y,n=10): # x为待分箱的变量,y为target变量.n为分箱数量
  2. total = y.count() # 计算总样本数
  3. bad = y.sum() # 计算坏样本数
  4. good = y.count()-y.sum() # 计算好样本数
  5. d1 = pd.DataFrame({'x':x,'y':y,'bucket':pd.qcut(x,n)}) # 用pd.cut实现等频分箱
  6. d2 = d1.groupby('bucket',as_index=True) # 按照分箱结果进行分组聚合
  7. d3 = pd.DataFrame(d2.x.min(),columns=['min_bin'])
  8. d3['min_bin'] = d2.x.min() # 箱体的左边界
  9. d3['max_bin'] = d2.x.max() # 箱体的右边界
  10. d3['bad'] = d2.y.sum() # 每个箱体中坏样本的数量
  11. d3['total'] = d2.y.count() # 每个箱体的总样本数
  12. d3['bad_rate'] = d3['bad']/d3['total'] # 每个箱体中坏样本所占总样本数的比例
  13. d3['badattr'] = d3['bad']/bad # 每个箱体中坏样本所占坏样本总数的比例
  14. d3['goodattr'] = (d3['total'] - d3['bad'])/good # 每个箱体中好样本所占好样本总数的比例
  15. d3['woe'] = np.log(d3['goodattr']/d3['badattr']) # 计算每个箱体的woe值
  16. iv = ((d3['goodattr']-d3['badattr'])*d3['woe']).sum() # 计算变量的iv值
  17. d4 = (d3.sort_values(by='min_bin')).reset_index(drop=True) # 对箱体从大到小进行排序
  18. print('分箱结果:')
  19. print(d4)
  20. print('IV值为:')
  21. print(iv)
  22. cut = []
  23. cut.append(float('-inf'))
  24. for i in d4.min_bin:
  25. cut.append(i)
  26. cut.append(float('inf'))
  27. woe = list(d4['woe'].round(3))
  28. return d4,iv,cut,woe

2. 等距分箱:从最小值到最大值之间,均分为 N 等份。 如果 A,B 为最小最大值, 则每个区间的长度为 W=(B−A)/N , 则区间边界值为A+W,A+2W,….A+(N−1)W 。这里只考虑边界,每个等份的实例数量可能不等。

  1. def bin_distince(x,y,n=10): # x为待分箱的变量,y为target变量.n为分箱数量
  2. total = y.count() # 计算总样本数
  3. bad = y.sum() # 计算坏样本数
  4. good = y.count()-y.sum() # 计算好样本数
  5. d1 = pd.DataFrame({'x':x,'y':y,'bucket':pd.cut(x,n)}) #利用pd.cut实现等距分箱
  6. d2 = d1.groupby('bucket',as_index=True) # 按照分箱结果进行分组聚合
  7. d3 = pd.DataFrame(d2.x.min(),columns=['min_bin'])
  8. d3['min_bin'] = d2.x.min() # 箱体的左边界
  9. d3['max_bin'] = d2.x.max() # 箱体的右边界
  10. d3['bad'] = d2.y.sum() # 每个箱体中坏样本的数量
  11. d3['total'] = d2.y.count() # 每个箱体的总样本数
  12. d3['bad_rate'] = d3['bad']/d3['total'] # 每个箱体中坏样本所占总样本数的比例
  13. d3['badattr'] = d3['bad']/bad # 每个箱体中坏样本所占坏样本总数的比例
  14. d3['goodattr'] = (d3['total'] - d3['bad'])/good # 每个箱体中好样本所占好样本总数的比例
  15. d3['woe'] = np.log(d3['goodattr']/d3['badattr']) # 计算每个箱体的woe值
  16. iv = ((d3['goodattr']-d3['badattr'])*d3['woe']).sum() # 计算变量的iv值
  17. d4 = (d3.sort_values(by='min_bin')).reset_index(drop=True) # 对箱体从大到小进行排序
  18. print('分箱结果:')
  19. print(d4)
  20. print('IV值为:')
  21. print(iv)
  22. cut = []
  23. cut.append(float('-inf'))
  24. for i in d4.min_bin:
  25. cut.append(i)
  26. cut.append(float('inf'))
  27. woe = list(d4['woe'].round(3))
  28. return d4,iv,cut,woe

3. 自定义分箱:

  1. def bin_self(x,y,cut): # x为待分箱的变量,y为target变量,cut为自定义的分箱(list)
  2. total = y.count() # 计算总样本数
  3. bad = y.sum() # 计算坏样本数
  4. good = y.count()-y.sum() # 计算好样本数
  5. d1 = pd.DataFrame({'x':x,'y':y,'bucket':pd.cut(x,cut)})
  6. d2 = d1.groupby('bucket',as_index=True) # 按照分箱结果进行分组聚合
  7. d3 = pd.DataFrame(d2.x.min(),columns=['min_bin'])
  8. d3['min_bin'] = d2.x.min() # 箱体的左边界
  9. d3['max_bin'] = d2.x.max() # 箱体的右边界
  10. d3['bad'] = d2.y.sum() # 每个箱体中坏样本的数量
  11. d3['total'] = d2.y.count() # 每个箱体的总样本数
  12. d3['bad_rate'] = d3['bad']/d3['total'] # 每个箱体中坏样本所占总样本数的比例
  13. d3['badattr'] = d3['bad']/bad # 每个箱体中坏样本所占坏样本总数的比例
  14. d3['goodattr'] = (d3['total'] - d3['bad'])/good # 每个箱体中好样本所占好样本总数的比例
  15. d3['woe'] = np.log(d3['goodattr']/d3['badattr']) # 计算每个箱体的woe值
  16. iv = ((d3['goodattr']-d3['badattr'])*d3['woe']).sum() # 计算变量的iv值
  17. d4 = (d3.sort_values(by='min_bin')).reset_index(drop=True) # 对箱体从大到小进行排序
  18. print('分箱结果:')
  19. print(d4)
  20. print('IV值为:')
  21. print(iv)
  22. woe = list(d4['woe'].round(3))
  23. return d4,iv,woe

 

  • 无监督分箱
  1. 卡方分箱
  2. 转载:https://github.com/tatsumiw/ChiMerge/blob/master/ChiMerge.py 

     

  1. def chi_bins(df,col,target,confidence=3.841,bins=20): # 设定自由度为1,卡方阈值为3.841,最大分箱数20
  2. total = df.target.count() #计算总样本数
  3. bad = df.target.sum() # 计算坏样本总数
  4. good = total - bad # 计算好样本总数
  5. total_bin = df.groupby([col])[target].count() # 计算每个箱体总样本数
  6. total_bin_table = pd.DataFrame({'total':total_bin}) #创建一个数据框保存结果
  7. bad_bin = df.groupby([col])[target].sum() # 计算每个箱体的坏样本数
  8. bad_bin_table = pd.DataFrame({'bad':bad_bin}) #创建一个数据框保存结果
  9. regroup = pd.merge(total_bin_table,bad_bin_table,left_index=True,right_index=True,how='inner') #组合total_bin 和 bad_bin
  10. regroup.reset_index(inplace=True)
  11. regroup['good'] = regroup['total']-regroup['bad'] #计算每个箱体的好样本数
  12. regroup = regroup.drop(['total'],axis=1) #删除total
  13. np_regroup = np.array(regroup) # 将regroup转为numpy
  14. # 处理连续没有正样本和负样本的区间,进行合并,以免卡方报错
  15. i = 0
  16. while (i <= np_regroup.shape[0] - 2):
  17. if ((np_regroup[i, 1] == 0 and np_regroup[i + 1, 1] == 0) or ( np_regroup[i, 2] == 0 and np_regroup[i + 1, 2] == 0)):
  18. np_regroup[i, 1] = np_regroup[i, 1] + np_regroup[i + 1, 1] # 正样本
  19. np_regroup[i, 2] = np_regroup[i, 2] + np_regroup[i + 1, 2] # 负样本
  20. np_regroup[i, 0] = np_regroup[i + 1, 0]
  21. np_regroup = np.delete(np_regroup, i + 1, 0)
  22. i = i - 1
  23. i = i + 1
  24. # 对相邻两个区间的值进行卡方计算
  25. chi_table = np.array([]) # 创建一个数组保存相邻两个区间的卡方值
  26. for i in np.arange(np_regroup.shape[0]-1):
  27. chi = ((np_regroup[i,1]*np_regroup[i+1,2]-np_regroup[i,2]*np_regroup[i+1,1])**2*\
  28. (np_regroup[i,1]+np_regroup[i,2]+np_regroup[i+1,1]+np_regroup[i+1,2]))/\
  29. ((np_regroup[i,1]+np_regroup[i,2])*(np_regroup[i+1,1]+np_regroup[i+1,2])*\
  30. (np_regroup[i,1]+np_regroup[i+1,1])*(np_regroup[i,2]+np_regroup[i+1,2]))
  31. chi_table = np.append(chi_table,chi)
  32. # 将卡方值最小的两个区间进行合并
  33. while(1): #除非设置break,否则会一直循环下去
  34. if(len(chi_table)<=(bins-1) or min(chi_table)>=confidence):
  35. break # 当chi_table的值个数小于等于(箱体数-1) 或 最小的卡方值大于等于卡方阈值时,循环停止
  36. chi_min_index = np.where(chi_table = min(chi_min_index))[0] # 找出卡方最小值的索引
  37. np_regroup[chi_min_index,1] = np_regroup[chi_min_index,1] + np_regroup[chi_min_index+1,1]
  38. np_regroup[chi_min_index,2] = np_regroup[chi_min_index,2] + np_regroup[chi_min_index+1,2]
  39. np_regroup[chi_min_index,0] = np_regroup[chi_min_index+1,0]
  40. np_regroup = np.delete(np_regroup,chi_min_index+1,axis=0)
  41. if (chi_min_index == np_regroup.shape[0]-1): # 当卡方最小值是最后两个区间时,计算当前区间和前一个区间的卡方值并替换
  42. chi_table[chi_min_index-1] = ((np_regroup[chi_min_index-1,1]*np_regroup[chi_min_index,2]-np_regroup[chi_min_index-1,2]\
  43. *np_regroup[chi_min_index,1])**2*(np_regroup[chi_min_index-1,1]+np_regroup[chi_min_index-1,2]\
  44. +np_regroup[chi_min_index,1]+np_regroup[chi_min_index,2]))/((np_regroup[chi_min_index-1,1]+\
  45. np_regroup[chi_min_index-1,2])*(np_regroup[chi_min_index,1]+np_regroup[chi_min_index,2])*\
  46. (np_regroup[chi_min_index-1,1]+np_regroup[chi_min_index,1])*(np_regroup[chi_min_index-1,2]+\
  47. np_regroup[chi_min_index,2]))
  48. chi_table = np.delete(chi_table,chi_min_index,axis=0) #删除替换前的卡方值
  49. else:
  50. # 计算合并后当前区间和前一个区间的卡方值并替换
  51. chi_table[chi_min_index-1] = ((np_regroup[chi_min_index-1,1]*np_regroup[chi_min_index,2]-np_regroup[chi_min_index-1,2]\
  52. *np_regroup[chi_min_index,1])**2*(np_regroup[chi_min_index-1,1]+np_regroup[chi_min_index-1,2]\
  53. +np_regroup[chi_min_index,1]+np_regroup[chi_min_index,2]))/((np_regroup[chi_min_index-1,1]+\
  54. np_regroup[chi_min_index-1,2])*(np_regroup[chi_min_index,1]+np_regroup[chi_min_index,2])*\
  55. (np_regroup[chi_min_index-1,1]+np_regroup[chi_min_index,1])*(np_regroup[chi_min_index-1,2]+\
  56. np_regroup[chi_min_index,2]))
  57. # 计算合并后当前区间和后一个区间的卡方值并替换
  58. chi_table[chi_min_index] = ((np_regroup[chi_min_index,1]*np_regroup[chi_min_index+1,2]-np_regroup[chi_min_index,2]\
  59. *np_regroup[chi_min_index+1,1])**2*(np_regroup[chi_min_index,1]+np_regroup[chi_min_index,2]\
  60. +np_regroup[chi_min_index+1,1]+np_regroup[chi_min_index+1,2]))/((np_regroup[chi_min_index,1]+\
  61. np_regroup[chi_min_index,2])*(np_regroup[chi_min_index+1,1]+np_regroup[chi_min_index+1,2])*\
  62. (np_regroup[chi_min_index,1]+np_regroup[chi_min_index+1,1])*(np_regroup[chi_min_index,2]+\
  63. np_regroup[chi_min_index+1,2]))
  64. chi_table = np.delete(chi_table,chi_min_index+1,axis=0) #删除替换前的卡方值
  65. # 将结果保存为一个数据框
  66. result_data = pd.DataFrame()
  67. result_data['col'] = [col]*np_regroup.shape[0] #结果第一列为变量名
  68. list_temp=[] # 创建一个空白的分组列
  69. for i in np.arange(np_regroup.shape[0]):
  70. if i==0: # 当为第一个箱体时
  71. x= '0' + ',' + str(np_regroup[i,0])
  72. elif i == np_regroup.shape[0]-1: # 当为最后一个箱体时
  73. x = str(np_regroup[i-1,0]) + '+'
  74. else:
  75. x = str(np_regroup[i-1,0]) + ',' + str(np_regroup[i,0])
  76. list_temp.append(x)
  77. result_data['bin'] = list_temp
  78. result_data['bad'] = np_regroup[:,1]
  79. result_data['good'] = np_regroup[:,2]
  80. result_data['bad_rate'] = result_data['bad']/total #计算每个箱体坏样本所占总样本比例
  81. result_data['badattr'] = result_data['bad']/bad #计算每个箱体坏样本所占坏样本总数的比例
  82. result_data['goodattr'] = result_data['good']/good #计算每个箱体好样本所占好样本总数的比例
  83. result_data['woe'] = np.log(result_data['goodattr']/result_data['badattr']) #计算每个箱体的woe值
  84. iv = ((result_data['goodattr']-result_data['badattr'])*result_data['woe']).sum() #计算每个变量的iv值
  85. print('分箱结果:')
  86. print(result_data)
  87. print('IV值为:')
  88. print(iv)
  89. return result_data,iv

关于作者:

本人就职于某金融科技公司从事风控建模工作,欢迎交流。

对于风控和机器学习感兴趣的童鞋可以关注下我的公众号:风控汪的数据分析之路。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/177700?site
推荐阅读
相关标签
  

闽ICP备14008679号