Python基于卡方值分箱算法的实现与优化

小鹿同学
小鹿同学
发布时间:2026-10-14 | 2人浏览过
原创

本文介绍Python中基于卡方值的分箱算法实现。通过初始等频分箱,确保每箱包含0和1标签,随后对卡方值最小的相邻箱进行合并,直至达到目标箱数。代码展示了如何计算各箱的IV值、坏比率及WOE值,适用于信用评分卡等场景的特征工程处理。

卡方分箱算法原理

该算法的核心原理较为直观:首先将变量初始划分为20个或更多的箱,确保每个箱内同时包含标签0和1。对于不包含完整标签分布的箱,执行向前合并操作;随后计算各箱的卡方统计量,对卡方值最小的相邻箱执行向后合并,具体实现代码如下。

核心代码实现


import pandas as pd
import numpy as np
import scipy
from scipy import stats
def chi_bin(DF,var,target,binnum=5,maxcut=20):
  '''
  DF:data
  var:variable
  target:target / label
  binnum: the number of bins output
  maxcut: initial bins number 
  '''
  
  data=DF[[var,target]]
  #equifrequent cut the var into maxcut bins
  data["cut"],breaks=pd.qcut(data[var],q=maxcut,duplicates="drop",retbins=True)
  #count 1,0 in each bin
  count_1=data.loc[data[target]==1].groupby("cut")[target].count()
  count_0=data.loc[data[target]==0].groupby("cut")[target].count()
  #get bins value: min,max,count 0,count 1
  bins_value=[*zip(breaks[:maxcut-1],breaks[1:],count_0,count_1)]
  #define woe
  def woe_value(bins_value):
    df_woe=pd.DataFrame(bins_value)
    df_woe.columns=["min","max","count_0","count_1"]
    df_woe["total"]=df_woe.count_1+df_woe.count_0
    df_woe["bad_rate"]=df_woe.count_1/df_woe.total
    df_woe["woe"]=np.log((df_woe.count_0/df_woe.count_0.sum())/(df_woe.count_1/df_woe.count_1.sum()))
    return df_woe
  #define iv
  def iv_value(df_woe):
    rate=(df_woe.count_0/df_woe.count_0.sum())-(df_woe.count_1/df_woe.count_1.sum())
    iv=np.sum(rate * df_woe.woe)
    return iv
  #make sure every bin contain 1 and 0
  ##first bin merge backwards
  for i in range(len(bins_value)):
    if 0 in bins_value[0][2:]:
      bins_value[0:2]=[(
        bins_value[0][0],
        bins_value[1][1],
        bins_value[0][2]+bins_value[1][2],
        bins_value[0][3]+bins_value[1][3])]
      continue
  ##bins merge forwards
    if 0 in bins_value[i][2:]:
      bins_value[i-1:i+1]=[(
        bins_value[i-1][0],
        bins_value[i][1],
        bins_value[i-1][2]+bins_value[i][2],
        bins_value[i-1][3]+bins_value[i][3])]
      break
    else:
      break
  
  #calculate chi-square merge the minimum chisquare    
  while len(bins_value)>binnum:
    chi_squares=[]
    for i in range(len(bins_value)-1):
      a=bins_value[i][2:]
      b=bins_value[i+1][2:]
      chi_square=scipy.stats.chi2_contingency([a,b])[0]
      chi_squares.append(chi_square)
  #merge the minimum chisquare backwards
    i = chi_squares.index(min(chi_squares))
               
    bins_value[i:i+2]=[(
      bins_value[i][0],
      bins_value[i+1][1],
      bins_value[i][2]+bins_value[i+1][2],
      bins_value[i][3]+bins_value[i+1][3])]
    
    df_woe=woe_value(bins_value)
    
  #print bin number and iv
    print("箱数:{},iv:{:.6f}".format(len(bins_value),iv_value(df_woe)))
  #return bins and woe information 
  return woe_value(bins_value)             

分箱效果与IV值分析

以下是算法运行的实际效果展示。

设定初始分箱数为10箱,最终目标分箱数为3箱。

调用示例


chi_bin(data,"age","SeriousDlqin2yrs",binnum=3,maxcut=10)

随着合并过程进行,箱数与对应的IV值变化如下: 箱数:8, iv: 0.184862 箱数:7, iv: 0.184128 箱数:6, iv: 0.179518 箱数:5, iv: 0.176980 箱数:4, iv: 0.172406 箱数:3, iv: 0.160015 最终生成的分箱统计信息如下表所示: | 箱号 | min | max | count_0 | count_1 | total | bad_rate | woe | |---|---|---|---|---|---|---|---| | 0 | 0.0 | 52.0 | 70293 | 7077 | 77370 | 0.091470 | -0.266233 | | 1 | 52.0 | 61.0 | 29318 | 1774 | 31092 | 0.057056 | 0.242909 | | 2 | 61.0 | 72.0 | 26332 | 865 | 27197 | 0.031805 | 0.853755 |

至此,关于Python基于卡方值分箱算法的实现示例介绍完毕。如需了解更多相关Python卡方分箱算法内容,欢迎查阅本站往期文章或继续浏览后续相关教程,感谢大家的支持。

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表