利用python建立客户流失预警模型(上)——数据处理部分

前言

看了不少文章受益匪浅,我也来在这里尝试总结一下自己运用到的,旨在和大家一起交流学习,请各位大佬多多指教。
下面进入正题,数据为商业银行的客户数据,将数据集分为训练集和测试机:
1、构建客户流失预警模型(bad_good为被解释变量);
2、通过相关变量构建客户画像系统;
3、根据两个模型,提出流失客户的应对策略
我将从数据处理和模型建立两个部分来展开。

数据处理

数据探索

一、读入数据

import pandas as pd
data=pd.read_csv('train.csv',sep=',',index_col=None)

二、数据探索
1、是否有缺失值

data.isnull().any().any()#查看data是否存在缺失值
v_null=data.isnull().any()#查看每个变量是否缺失
v_null=v_null[v_null==True].reset_index()#取出有缺失值的变量
v_null=data[v_null['index']]#取出有缺失值
v_isnull().sum()
#若缺失值比较多使用fillna进行填充
data['变量名'].fillna(data['变量名'].mean())

2、查看是否有逻辑上的重复数据,即重复样本

len(np.unique(data['主键名']))#若等于样本数则没有重复,否则运行下列代码
sta=data['主键名'].value_counts()#统计出现次数
sta=sta[sta>1]#取出重复样本,并予以删除

3、查看离散与连续数据分布

dis=data[离散变量].apply(pd.value_counts)#查看离散连续变量
con=data[连续变量].describe()

在此数据中看到性别有X的样本,需要后续处理,同时大多FLAG变量属性不统一,存在N、Y、0、1共存的情况,需要后续将其统一
4、变量整理与衍生变量
首先从变量层面对原有的628个变量做整理,得到离散变量、交易金额和笔数最大最小变量、存在本期3个月6个月变量以及其他变量
1)离散变量:
首先进行属性的统一,使用map将之映射为0或1
对于其中基金,将所有基金标志合并,只要有一类为1,即最终为1
对于其中国债,将所有国债标志合并,只要有一类为1,即最终为1
对于客户卡,按带有白金、钻石以及无限卡作为VIP,其余为非VIP
2)最大最小变量:
删除最小变量,因为其仅有极少数样本有值且值都不大,研究意义不大
有的变量可以整合成一个变量,将其取最大来表示
新衍生变量转入转出金额最大为不同途径金额最大和
3)本期3个月6个月变量:
首先,考虑仅保留6个月变量作为较稳定的衡量指标
新衍生变量活跃度:本期除以3个月比率和3个月除以6个月比率以7:3权重综合得到
新衍生变量转入转出比率
新衍生变量资产负债比
新衍生变量集中度,不同渠道或产品的最大值以及最大值对应的活跃度:分别对于交易金额与笔数、投资金额、转入转出金额与笔数进行构造
4)其他变量:
有的无6个月数据但可以根据其他变量计算出6个月数据,故将其他中的本期与3个月数据删除,还可以删除一些对业务没有影响的变量
5)当数据量大时,每做完一次操作可考虑保存数据,否则每次运行耗费大量时间

#对分类与连续变量做统计进行清洗并初步筛选变量
all_names=data.columns.values.tolist()                                              #取所有变量名
all_names.remove('bad_good')
remove_variable=[]
data['GENDER']=data['GENDER'].map({1:'1',2:'2','2':'2','1':'1','X':'X'})
for i in range(len(all_names)):                                                      #删除值全一样的变量
    if len(np.unique(data[all_names[i]]))==1:
        remove_variable.append(all_names[i])
data=data.drop(remove_variable,axis=1,inplace=False)
all_names=list(set(all_names).difference(set(remove_variable)))
del remove_variable

discrete_variable=['OPEN_ORG_NUM','IDF_TYP_CD','GENDER']                             #取离散变量名
for i in range(len(all_names)):                                                      #补全离散变量名
    if all_names[i].upper().endswith('FLAG'):
        discrete_variable.append(all_names[i])
IND_variable=[]
for i in range(len(all_names)):                                                      #对应最大渠道或投资产品
    if all_names[i].upper().endswith('IND'):
        IND_variable.append(all_names[i])
data_ind=data[IND_variable]
data=data.drop(IND_variable,axis=1,inplace=False)
continuous_variable=list(set(all_names).difference(set(
                 discrete_variable)).difference(set(IND_variable)))           #取连续变量名
sta_data=data[discrete_variable].apply(pd.value_counts)                            #查看变量频次
con_data=data[continuous_variable].describe()

5、值替换
1)根据训练集中男女性别比对X值进行随机替换

sum1=data.groupby('GENDER').size()[0]                                               #查看性别频次,获取男女比
sum2=data.groupby('GENDER').size()[1]                                               #大的设置为sum1,小的为sum2
data_gender=data[data['GENDER']=='X'].reset_index()['index'].tolist()
for i in data_gender:                                                          
	random.seed(6)      #男女比随机生成性别                                                 
    data.loc[i,'GENDER']=str(math.ceil(random.randint(1,sum1+sum2)/sum1))
data['GENDER']=train['GENDER'].map({'1':1,'2':2})
sta_data=data[discrete_variable].apply(pd.value_counts)

2)对之前数据探索中离散变量中属性较多的变量利用贝叶斯平滑得到流失率,并观察人为给定划分界限,将离散变量属性合并,得到属性较少的变量

转化率(CTR)预测的贝叶斯平滑

def est(data):                                                                       #计算贝叶斯平滑参数
    datamean=sum(data.iloc[:,1]*data.iloc[:,3])/sum(data.iloc[:,1])
    datavar=sum(data.iloc[:,1]*pow(data.iloc[:,3]-datamean,2))/(sum(data.iloc[:,1])-1)
    a=datamean*(datamean*(1-datamean)/datavar-1)
    b=(1-datamean)*(datamean*(1-datamean)/datavar-1)
    return a,b

5.筛选变量
1)初步筛选:删除所有样本的值均相同的变量
2)筛选离散变量(离散与离散关系):一般使用卡方检验,也可以使用计算WOE与IV,通过IV的大小来判断,一般小于0.2的删除,大于0.6的与因变量特别相关,考虑是否合适,其余可以保留

#使用卡方检验筛选分类变量
dis_chi2=pd.DataFrame({'name':discrete_variable,'p_value':chi2(train[discrete_variable],train.bad_good)[1]})             #建表
dis_chi2=dis_chi2[dis_chi2.p_value<0.05].sort_index(by='p_value').reset_index().drop('index',axis=1,inplace=False)  #卡方检验显著变量与p值

left_dis=dis_chi2.name.tolist()                                                      #剩下的变量
dis_remove=list(set(discrete_variable).difference(set(left_dis)))                    #删掉的变量
all_names=list(set(all_names).difference(set(dis_remove)))                           #删除变量
data=data.drop(dis_remove,axis=1,inplace=False)
del dis_remove,discrete_variable


3)筛选连续变量(连续与离散关系):一般使用方差分析,看离散变量属性的均值有无差异,但方差分析需要满足正态性、方差齐性以及独立性条件,需要先KS检验正态性以及检验方差齐性才可进行方差分析;若不满足则考虑使用曼-惠特尼U检验。(也可以使用相关性分析或者MI互信息来考察变量之间的相关程度)

#筛选连续变量
data0=data[data.bad_good==0]
data1=data[data.bad_good==1]
sel_con=pd.DataFrame({'name':continuous_variable,'ks0p_value':1,'ks1p_value':1,
                      'lp_value':1,'ap_value':1,'Up_value':1})
for i in range(len(continuous_variable)):                                            #检验正态性、方差齐性
    d0=data0[continuous_variable[i]]                                              #方差分析与非参数检验
    d1=data1[continuous_variable[i]]                                              #筛选变量
    args=[d0,d1]
    sel_con.loc[i,'ks0p_value']=kstest(data0[continuous_variable[i]],'norm')[1]
    sel_con.loc[i,'ks1p_value']=kstest(data1[continuous_variable[i]],'norm')[1]
    sel_con.loc[i,'lp_value']=stats.levene(*args)[1]
    sel_con.loc[i,'ap_value']=anova_lm(ols(continuous_variable[i]+'~bad_good',data).fit()).iloc[0,4]
    sel_con.loc[i,'Up_value']=stats.mannwhitneyu(d0,d1)[1]
con_U=sel_con[sel_con.Up_value<0.05].sort_index(by='Up_value').reset_index()[['name','Up_value']]
left_con=con_U.name.tolist()  #剩下的变量

6.数据归一或标准化
主要是为了消除变量之间数量级的差异,对于回归以及需要计算距离的问题尤为重要,也为后续使用过采样方法做准备。主要有归一化、标准化或取对数这些方法。

#数据归一化
data_all=data.append(test).reset_index()
data_all=data_all.drop('index',axis=1,inplace=False)
data_all=pd.DataFrame(preprocessing.MinMaxScaler().fit_transform(data_all.values),columns=data_all.columns)
train=data_all.iloc[:len(data),:]
test=data_all.iloc[len(data):,:]
del data_all

7.采样方法
可以进行过采样、欠采样、双采样的方法来处理不平衡数据,最为普通的就是简单的对原样本进行少类重抽样或者多类随机删除的方法;过采样较新的方法可由SMOTE方法人工合成大量少类样本。

#过采样,可改变种子
x_train,y_train=SMOTE(random_state=666,kind='borderline2').fit_sample(train.iloc[:,1:],train.bad_good)
y_train=list(map(int,y_train))
#x_train=train.iloc[:,1:].values
#y_train=train.bad_good.values
x_test=test.iloc[:,1:].values
y_test=test.bad_good.values