#简介
本文通过使用LendingClub的数据,采用卡方分箱(ChiMerge)、WOE编码、计算IV值、单变量和多变量(VIF)分析,然后使用逻辑回归模型进行训练,在变量筛选时也可尝试添加L1约束或通过随机森林筛选变量,最后进行模型评估。
######关键词:卡方分箱,WOE,IV值,变量分析,逻辑回归
####一、数据预处理
数据清洗:数据选择、格式转换、缺失值填补
由于贷款期限(term)有多个种类,申请评分卡模型评估的违约概率必须在统一的期限中,并且不宜太长,因此选择36months的数据作为本次建模数据,60%训练,40%测试。
```
folderOfData = os.path.join(os.getcwd(), 'data')
allData = pd.read_csv(os.path.join(folderOfData,'application.csv'),header = 0, encoding = 'latin1')
allData['term'] = allData['term'].apply(lambda x: int(x.replace(' months','')))
# 处理标签:Fully Paid是正常用户;Charged Off是违约用户
allData['y'] = allData['loan_status'].map(lambda x: int(x == 'Charged Off'))
allData1 = allData.loc[allData.term == 36]
trainData, testData = train_test_split(allData1,test_size=0.4)
```
进一步清洗:
1. 将int_rate利息转换为小数形式
2. 将emp_length处理为:10+为11,<1为0,空为-1
3. desc为有记录和无记录两种情况
4. 日期处理
5. 两个日期之间月数计算
```
# 将带%的百分比变为浮点数
trainData['int_rate_clean'] = trainData['int_rate'].map(lambda x: float(x.replace('%',''))/100)
# 将工作年限进行转化,否则影响排序
trainData['emp_length_clean'] = trainData['emp_length'].map(CareerYear)
# 将desc的缺失作为一种状态,非缺失作为另一种状态
trainData['desc_clean'] = trainData['desc'].map(DescExisting)
# 处理日期。earliest_cr_line的格式不统一,需要统一格式且转换成python的日期
trainData['app_date_clean'] = trainData['issue_d'].map(lambda x: ConvertDateStr(x))
trainData['earliest_cr_line_clean'] = trainData['earliest_cr_line'].map(lambda x: ConvertDateStr(x))
# 处理mths_since_last_delinq。注意原始值中有0,所以用-1代替缺失
trainData['mths_since_last_delinq_clean'] = trainData['mths_since_last_delinq'].map(lambda x:MakeupMissing(x))
trainData['mths_since_last_record_clean'] = trainData['mths_since_last_record'].map(lambda x:MakeupMissing(x))
trainData['pub_rec_bankruptcies_clean'] = trainData['pub_rec_bankruptcies'].map(lambda x:MakeupMissing(x))
```
####二、变量衍生和挑选
- 衍生:
1. 考虑申请额度与收入的占比
2. 考虑earliest_cr_line到申请日期的跨度,计算月份数
```
# 考虑申请额度与收入的占比
trainData['limit_income'] = trainData.apply(lambda x: x.loan_amnt / x.annual_inc, axis = 1)
# 考虑earliest_cr_line到申请日期的跨度,计算月份数
trainData['earliest_cr_to_app'] = trainData.apply(lambda x: MonthGap(x.earliest_cr_line_clean,x.app_date_clean), axis = 1)
```
- 挑选:
我们初步挑选变量如下,分为两类:数值型(连续型)的和类别型的变量。
```
num_features = ['int_rate_clean','emp_length_clean','annual_inc', 'dti', 'delinq_2yrs', 'earliest_cr_to_app','inq_last_6mths', \
'mths_since_last_record_clean', 'mths_since_last_delinq_clean','open_acc','pub_rec','total_acc','limit_income','earliest_cr_to_app']
cat_features = ['home_ownership', 'verification_status','desc_clean', 'purpose', 'zip_code','addr_state','pub_rec_bankruptcies_clean']
```
####三、卡方分箱法
采用卡方(ChiMerge)分箱,要求分箱完成之后:
1. 不超过5箱(本模型默认不超过5箱)
2. 坏样本率(Bad Rate)单调
3. 每箱同时包含好坏样本
4. 如有特殊值如-1单独成一箱,此箱不参与Bad Rate单调性检验
连续型的变量可以直接进行分箱,对于类别型的变量分为以下几种情况:
1. 当类别型变量取值比较多时(本例中大于5),先用bad rate 进行编码,然后放入连续型变量列表中,使用连续型变量分箱的方法进行分箱。
2. 当取值较少时(本例中小于等于5),分两种情况:
(1)如果每种类别同时包含好坏样本,则无需分箱;
(2)如果有类别只包含好坏样本的一种,则需要合并;
具体操作如下:
第一步,检查类别型变量中,哪些变量取值超过5。
```
more_value_features = []
less_value_features = []
# 第一步,检查类别型变量中,哪些变量取值超过5
for var in cat_features:
valueCounts = len(set(trainData[var]))
print valueCounts
if valueCounts > 5:
more_value_features.append(var) #取值超过5的变量,需要bad rate编码,再用卡方分箱法进行分箱
else:
less_value_features.append(var)
```
第二步,当取值&l