连续值分箱

def chi_bins(df,col,target,confidence=3.841,bins=20): # 设定自由度为1,卡方阈值为3.841,最大分箱数20

    total = df[target].count()  #计算总样本数

    bad = df[target].sum()   # 计算坏样本总数

    good = total - bad      # 计算好样本总数

    total_bin = df.groupby([col])[target].count()  # 计算每个箱体总样本数

    total_bin_table = pd.DataFrame({'total':total_bin}) #创建一个数据框保存结果

    bad_bin = df.groupby([col])[target].sum()  # 计算每个箱体的坏样本数

    bad_bin_table = pd.DataFrame({'bad':bad_bin})  #创建一个数据框保存结果

    regroup = pd.merge(total_bin_table,bad_bin_table,left_index=True,right_index=True,how='inner')  #组合total_bin 和 bad_bin

    regroup.reset_index(inplace=True)  

    regroup['good'] = regroup['total']-regroup['bad'] #计算每个箱体的好样本数

    regroup = regroup.drop(['total'],axis=1)  #删除total

    np_regroup = np.array(regroup)  # 将regroup转为numpy

    

    # 处理连续没有正样本和负样本的区间,进行合并,以免卡方报错

    i = 0

    while (i <= np_regroup.shape[0] - 2):

        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)):

            np_regroup[i, 1] = np_regroup[i, 1] + np_regroup[i + 1, 1]  # 正样本

            np_regroup[i, 2] = np_regroup[i, 2] + np_regroup[i + 1, 2]  # 负样本

            np_regroup[i, 0] = np_regroup[i + 1, 0]

            np_regroup = np.delete(np_regroup, i + 1, 0)

            i = i - 1

        i = i + 1

    

    # 对相邻两个区间的值进行卡方计算

    chi_table = np.array([]) # 创建一个数组保存相邻两个区间的卡方值

    for i in np.arange(np_regroup.shape[0]-1):

        chi = ((np_regroup[i,1]*np_regroup[i+1,2]-np_regroup[i,2]*np_regroup[i+1,1])**2*\

               (np_regroup[i,1]+np_regroup[i,2]+np_regroup[i+1,1]+np_regroup[i+1,2]))/\

               ((np_regroup[i,1]+np_regroup[i,2])*(np_regroup[i+1,1]+np_regroup[i+1,2])*\

                (np_regroup[i,1]+np_regroup[i+1,1])*(np_regroup[i,2]+np_regroup[i+1,2]))

        chi_table = np.append(chi_table,chi)

    

    # 将卡方值最小的两个区间进行合并          

    while(1):  #除非设置break,否则会一直循环下去

        if(len(chi_table)<=(bins-1) or min(chi_table)>=confidence):

            break  # 当chi_table的值个数小于等于(箱体数-1) 或 最小的卡方值大于等于卡方阈值时,循环停止

        chi_min_index = np.where(chi_table == min(chi_table))[0] # 找出卡方最小值的索引

        np_regroup[chi_min_index,1] = np_regroup[chi_min_index,1] + np_regroup[chi_min_index+1,1]

        np_regroup[chi_min_index,2] = np_regroup[chi_min_index,2] + np_regroup[chi_min_index+1,2]

        np_regroup[chi_min_index,0] = np_regroup[chi_min_index+1,0]

        np_regroup = np.delete(np_regroup,chi_min_index+1,axis=0)

        

        if (chi_min_index == np_regroup.shape[0]-1): # 当卡方最小值是最后两个区间时,计算当前区间和前一个区间的卡方值并替换

            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]\

                                         *np_regroup[chi_min_index,1])**2*(np_regroup[chi_min_index-1,1]+np_regroup[chi_min_index-1,2]\

                                         +np_regroup[chi_min_index,1]+np_regroup[chi_min_index,2]))/((np_regroup[chi_min_index-1,1]+\

                                         np_regroup[chi_min_index-1,2])*(np_regroup[chi_min_index,1]+np_regroup[chi_min_index,2])*\

                                         (np_regroup[chi_min_index-1,1]+np_regroup[chi_min_index,1])*(np_regroup[chi_min_index-1,2]+\

                                          np_regroup[chi_min_index,2]))

            chi_table = np.delete(chi_table,chi_min_index,axis=0)  #删除替换前的卡方值

        else:

            # 计算合并后当前区间和前一个区间的卡方值并替换

            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]\

                                         *np_regroup[chi_min_index,1])**2*(np_regroup[chi_min_index-1,1]+np_regroup[chi_min_index-1,2]\

                                         +np_regroup[chi_min_index,1]+np_regroup[chi_min_index,2]))/((np_regroup[chi_min_index-1,1]+\

                                         np_regroup[chi_min_index-1,2])*(np_regroup[chi_min_index,1]+np_regroup[chi_min_index,2])*\

                                         (np_regroup[chi_min_index-1,1]+np_regroup[chi_min_index,1])*(np_regroup[chi_min_index-1,2]+\

                                          np_regroup[chi_min_index,2]))

            # 计算合并后当前区间和后一个区间的卡方值并替换

            chi_table[chi_min_index] = ((np_regroup[chi_min_index,1]*np_regroup[chi_min_index+1,2]-np_regroup[chi_min_index,2]\

                                         *np_regroup[chi_min_index+1,1])**2*(np_regroup[chi_min_index,1]+np_regroup[chi_min_index,2]\

                                         +np_regroup[chi_min_index+1,1]+np_regroup[chi_min_index+1,2]))/((np_regroup[chi_min_index,1]+\

                                         np_regroup[chi_min_index,2])*(np_regroup[chi_min_index+1,1]+np_regroup[chi_min_index+1,2])*\

                                         (np_regroup[chi_min_index,1]+np_regroup[chi_min_index+1,1])*(np_regroup[chi_min_index,2]+\

                                          np_regroup[chi_min_index+1,2]))

            chi_table = np.delete(chi_table,chi_min_index+1,axis=0) #删除替换前的卡方值

        

# 将结果保存为一个数据框

    result_data = pd.DataFrame()

    result_data['col'] = [col]*np_regroup.shape[0] #结果第一列为变量名

    list_temp=[] # 创建一个空白的分组列

    for i in np.arange(np_regroup.shape[0]): 

        if i==0:  # 当为第一个箱体时

            x= '0' + ',' + str(np_regroup[i,0])

        elif i == np_regroup.shape[0]-1: # 当为最后一个箱体时

            x = str(np_regroup[i-1,0]) + '+'

        else:

            x = str(np_regroup[i-1,0]) + ',' + str(np_regroup[i,0])

        list_temp.append(x)

    result_data['bin'] = list_temp

    result_data['bad'] = np_regroup[:,1]

    result_data['good'] = np_regroup[:,2]

    result_data['bad_rate'] = result_data['bad']/(total)  #计算每个箱体坏样本所占总样本比例

    result_data['badattr'] = result_data['bad']/(bad)     #计算每个箱体坏样本所占坏样本总数的比例

    result_data['goodattr'] = result_data['good']/(good)  #计算每个箱体好样本所占好样本总数的比例

    result_data['woe'] = np.log(result_data['goodattr']/(result_data['badattr']))  #计算每个箱体的woe值

    iv = ((result_data['goodattr']-result_data['badattr'])*result_data['woe']).sum()  #计算每个变量的iv值

    print('分箱结果:')

    print(result_data)

    print('IV值为:')

    print(iv)

return result_data,iv

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值