好久没有更新了,emm…
之前给大家分享过卡方分箱,想了解的可以点击下面链接。
卡方分箱
那么,今天给大家分享在评分卡中另外一种常见的分箱方法best-ks,首先我们先来了解一下,best-ks是如何分箱的。
1.分箱逻辑:
步骤:
将变量排序后,计算出ks最大的值,即为切点,将数据分成2部分,分别对这2部分数据继续计算ks,找切点,直至达到箱子上限数
分箱后的KS值<=分箱前的KS值
2.实现代码:
################################### 计算ks值
def cal_ks(regroup,min_samples):
'''
args:
regroup:统计每个值的好、坏数,df
min_samples:最小样本数
return:
ks_dict:
ks_range:区间范围
max_ks_cutoff:ks值最大的切点
max_ks_value:最大的ks值
'''
ks_range = str(regroup['feature_name'].agg(['min','max']).values.tolist()) # 最小值、最大值
if (len(regroup) == 1) or (regroup['CntRec'].sum() < min_samples) or (regroup['CntBad'].sum()==0) or (regroup['CntGood'].sum()==0):
# 出现以下四种情况将不进行分箱:只有一种值、总样本数较少、坏样本数为0、好样本数为0
ks_dict = {ks_range:[-999999,-999999]}
else:
###### 1.计算ks
regroup.index = range(len(regroup))
regroup['CntCumBad'] = regroup['CntBad'].cumsum()
regroup['CntCumGood'] = regroup['CntGood'].cumsum()
regroup['CntCumBadRate'] = regroup['CntCumBad'] / regroup['CntBad'].sum()
regroup['CntCumGoodRate'] = regroup['CntCumGood'] / regroup['CntGood'].sum()
regroup['KS'] = abs(regroup['CntCumBadRate'] - regroup['CntCumGoodRate'])
###### 2.找出ks最大的值、切点、范围
max_ks_value = regroup['KS'].max() # ks值
max_ks_cutoff = regroup.loc[regroup['KS'].argmax(),'feature_name'] # 切点
ks_dict = {ks_range:[max_ks_cutoff,max_ks_value]}
return ks_dict
################################### 挑选出best-ks的切点
def best_ks_cut_bin(data,cols_num,label,max_interval=5,min_samples_rate=0.05):
'''
args:
data:数据源,df
cols_num:数值型字段,列表
label:标签,string
max_interval=5:最大分箱数,int
min_samples_rate=0.05:每个箱子的最小样本数
return:
cols_num_points:各数值变量的切点,字典
'''
cols_num_points = {}
for col in cols_num:
print(col)
if len(data[col].unique()) < max_interval: # 如果数据的有限值个数 < 最大分箱数,则不分箱,反之分箱
col_points = data[col].unique()
else:
min_samples = len(data)*min_samples_rate
# 统计每个值的好、坏数
regroup = data.groupby([col])[label].agg(['count','sum']).reset_index().rename(columns={col:'feature_name','count':'CntRec','sum':'CntBad'})
regroup['CntGood'] = regroup['CntRec'] - regroup['CntBad']
# 第一次划分切点
ks_dict = cal_ks(regroup,min_samples)
col_points = [list(ks_dict.values())[0][0]]
# 循环划分
while len(col_points) < max_interval-1:
best_ks_value = -999999
best_ks_cutoff = -999999
i = 0
# 对于每个区间进行再次划分
while i <= len(col_points):
###### 1.提取每个区间内的数据
if i==0:
regroup_per = regroup[regroup['feature_name'] <= col_points[i]]
elif i==len(col_points):
regroup_per = regroup[regroup['feature_name'] > col_points[i-1]]
else:
regroup_per = regroup[(regroup['feature_name'] > col_points[i-1]) & (regroup['feature_name'] <= col_points[i])]
###### 2.判断该区间有没有计算过ks,如果计算过,直接提取相应的ks,反之计算
ks_range = str(regroup_per['feature_name'].agg(['min','max']).values.tolist()) # 区间范围
if ks_range in ks_dict.keys():
max_ks_cutoff, max_ks_value = ks_dict[ks_range]
else:
ks_dict_i = cal_ks(regroup_per,min_samples)
max_ks_cutoff, max_ks_value = list(ks_dict_i.values())[0]
ks_dict.update(ks_dict_i)
###### 3.提取ks最大的切点
if max_ks_value > best_ks_value:
best_ks_cutoff, best_ks_value = max_ks_cutoff, max_ks_value
i = i + 1 # 循环计算下一个区间的ks
# 循环结束后,添加ks最大的切点
col_points.append(best_ks_cutoff)
col_points = sorted(col_points)
# 切点结果处理
col_points = list(set(col_points) - set([-999999]))
cols_num_points[col] = col_points
return cols_num_points
详细内容请关注我的公众号~