python 分箱的一种方法
2018.08.02
R语言中有smbining可以进行最优分箱,python中分箱如果既要考虑箱体个数,分箱后信息量大小,也要考虑单调性等其他因素。
这里给出一种简单的通过IV值来选择如果分箱的方法。
下面是按照分位数来分的,还可以按照卡房分箱,决策树分箱等。
参照toad(由厚本金融开发的较标准的评分卡开发开源包)的分箱方式。
class CUT_():
def __init__(self,data,catecory,target):
self.data = copy.deepcopy(data)
self.catedat = self.data[catecory]
self.catedat = self.catedat.map(float)
self.y = self.data['y'].map(float)
self.y = self.y.map(int)
self.catecory = catecory
self.target = target
@staticmethod
def ivsum(data,y,binlist,catecory,target):
tep_iv = OrderedDict()
dat = data.map(lambda x:float(x))
dat_tem = dat[dat!='nan']
bins = binlist
datcut = pd.cut( dat_tem,
[-float("inf")]+bins+[float("inf")])
dat = pd.concat([datcut,dat[dat=='nan']],axis=0)
dat = dat.sort_index()
dat = dat.map(str)
labels = list(set(dat))
dat2 = pd.concat([dat,y],axis=1)
dat2[target] = dat2[target].map(str)
C=(dat2[target]=='0').sum()
D=(dat2[target]=='1').sum()
for j in labels:
temdat=list(dat2[dat2[catecory]==j][target])
if len(temdat)>0:
A=len([x for x in temdat if x =='0'])
B=len([x for x in temdat if x =='1'])
if A!=0 and B!=0:
iv=round((B/D-A/C)*(log((B/D)/(A/C))),6)
tep_iv.update({j:iv})
if A==0 and B!=0:
A=A+1
iv=round((B/D-A/C)*(log((B/D)/(A/C))),6)
tep_iv.update({j:iv})
if B==0 and A!=0:
B=B+1
iv=round((B/D-A/C)*(log((B/D)/(A/C))),6)
tep_iv.update({j:iv})
if B==0 and A==0:
B=B+1
A=A+1
iv=round((B/D-A/C)*(log((B/D)/(A/C))),6)
tep_iv.update({j:iv})
if len(temdat)==0:
tep_iv.update({j:np.nan})
dataivsum = sum([x for x in tep_iv.values() if pd.isnull(x)==0])
minrate = dat2[catecory].value_counts().min()/dat2.shape[0]
return dataivsum,labels,bins,minrate
def bin_fun(self):
tem1 = self.catedat[self.catedat!='nan'].sort_values()
tem2 = self.catedat[(self.catedat!=''nan'')&(self.catedat!=-0)].sort_values()
var_set1 = pd.Series(sorted(list(set(tem1))))
var_set2 = pd.Series(sorted(list(set(tem2))))
cut_ = {'c1':[tem1.quantile(.25),tem1.quantile(.5),tem1.quantile(.75)],
'c2' : [tem1.quantile(.33),tem1.quantile(.66)],
'c3' : [tem1.quantile(.5)],
'c4' : [0],
'c5' : sorted([0,tem2.quantile(.25),tem2.quantile(.5),tem2.quantile(.75)]),
'c6' : sorted([0,tem2.quantile(.33),tem2.quantile(.66)]),
'c7': sorted([0,tem2.quantile(.5)]),
'c8': sorted([var_set1.quantile(.25),var_set1.quantile(.5),var_set1.quantile(.75)]),
'c9' : sorted([var_set1.quantile(.33),var_set1.quantile(.66)]),
'c10' : sorted([var_set1.quantile(.5)]),
'c11' : [0],
'c12' : sorted([0,var_set2.quantile(.25),var_set2.quantile(.5),var_set2.quantile(.75)]),
'c13' : sorted([0,var_set2.quantile(.33),var_set2.quantile(.66)]),
'c14': sorted([0,var_set2.quantile(.5)]),
'c15':[ mode(tem1)[0][0]], 'c16':[ mode(tem2)[0][0]],
'c17':[tem1.mean()],
'c18': [tem2.mean()],
'c19':[var_set1.mean()],
'c20':[ var_set2.mean()]}
# 这里用的分位数对应的值来分,还可以尝试其他不同的分法,
# 分法越多,相对最优的分箱效果越好。当然越来越慢。。。笨办法就没有考虑其他的了
self.iv_ = dict()
for i in cut_.keys():
if len(cut_[i])!=len(set(cut_[i])):
pass
else:
dataivsum,labels,bins,minrate = self.ivsum(self.catedat,self.y,cut_[i],self.catecory,self.target)
if minrate<0.005:
# 如果最小类的比例小于千分之五,则不输出
pass
else:
self.iv_[i] = dataivsum
best = [k for k,v in self.iv_.items() if v==max(self.iv_.values())]
## 输出IV值最大的分法
if len(best)>0:
self.best_cut = cut_[best[0]]
else:
print('找不到最优分法')
self.best_cut = [0]
def main(self):
self.bin_fun()
return self.iv_,self.best_cut
网友评论