金融信贷风控实战(一)

    • 代码实战
      • 1 数据
      • 2 特征工程
        • 2.1 数据清洗
          • 2.1.1 数据格式处理
          • 2.1.2 缺失值
          • 2.1.3 标签处理和选择数据
        • 2.2 特征衍生
        • 2.3 分箱
    • 参考资料

代码实战

1 数据

来自于lending club
这里写图片描述

print (data.shape) #(39785, 25)
data.info()
'''
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 39785 entries, 0 to 39784
Data columns (total 25 columns):
member_id                 39785 non-null int64
loan_amnt #贷款额度                39785 non-null int64
term                      39785 non-null object
loan_status               39785 non-null object
int_rate                  39785 non-null object
emp_length                38707 non-null object
home_ownership            39785 non-null object
annual_inc #年收入                39785 non-null float64
verification_status       39785 non-null object
desc                      26818 non-null object
purpose                   39785 non-null object
title                     39774 non-null object
zip_code                  39785 non-null object
addr_state                39785 non-null object
dti                       39785 non-null float64
delinq_2yrs               39785 non-null int64
inq_last_6mths            39785 non-null int64
mths_since_last_delinq    14058 non-null float64
mths_since_last_record    2791 non-null float64
open_acc                  39785 non-null int64
pub_rec                   39785 non-null int64
total_acc                 39785 non-null int64
pub_rec_bankruptcies      39088 non-null float64
issue_d #放款日期                  39785 non-null object
earliest_cr_line #信用报告最早日期          39785 non-null object
dtypes: float64(5), int64(7), object(13)
'''

2 特征工程

特征工程包括:

  1. 数据清洗
  2. 特征衍生
  3. 特征编码
  4. 特征筛选

2.1 数据清洗

主要的处理包括:

  1. 数据格式处理,包括日期以及字符型转为整型
  2. 缺失值处理
  3. 处理标签和选择数据
2.1.1 数据格式处理
  • int_rate由str转为float
data['int_rate']=data.int_rate.apply(lambda x:float(x.replace("%",""))/100)
  • 日期处理
    Python time strftime() Method
    这里写图片描述
import datetime
import datetime
def ConvertDateStr(x):mth_dict={'Jan':1,'Feb':2,'Mar':3,'Apr':4,'May':5,'Jun':6,'Jul':7,'Aug':8,'Sep':9,'Oct':10,'Nov':11,'Dec':12}yr= int(x[4:6])if yr>17:yr=yr+1900else:yr=yr+2000mth=mth_dict[x[:3]]return datetime.datetime(yr,mth,1)data['app_date_clean']=data['issue_d'].map(lambda d:ConvertDateStr(d))
data['earliest_cr_line_clean']=data['earliest_cr_line_clean'].map(lambda d:ConvertDateStr(d))
  • emp_length
data.emp_length=data.emp_length.fillna("n/a")
data.emp_length.value_counts()
'''
10+ years    8899
< 1 year     4590
2 years      4394
3 years      4098
4 years      3444
5 years      3286
1 year       3247
6 years      2231
7 years      1775
8 years      1485
9 years      1258
n/a          1078
Name: emp_length, dtype: int64
'''
import re
def CareerYear(x):if x.find('n/a')>-1:return -1elif x.find('10+')>-1:return 11elif x.find('< 1')>-1:return 0else:return int(re.sub("\D","",x)) # \D matches any non-digit characterdata.emp_length=data.emp_length.apply(CareerYear)
data.emp_length.value_counts()
'''
11    88990     45902     43943     40984     34445     32861     32476     22317     17758     14859     1258
-1     1078
Name: emp_length, dtype: int64
'''
2.1.2 缺失值
total=data.isnull().sum().sort_values(ascending=False)
percent=(data.isnull().sum()/data.isnull().count()).sort_values(ascending=False)
missing_data=pd.concat([total,percent],axis=1,keys=['Total','Percent'])
missing_data.head(10)

这里写图片描述

  • 缺失值填补
def MakeupMissing(x):if np.isnan(x):return -1else:return x# 处理mths_since_last_delinq。注意原始值中有0,所以用-1代替缺失
data['mths_since_last_delinq_clean'] = data['mths_since_last_delinq'].map(lambda x:MakeupMissing(x))
data['mths_since_last_record_clean'] = data['mths_since_last_record'].map(lambda x:MakeupMissing(x))
data['pub_rec_bankruptcies_clean'] = data['pub_rec_bankruptcies'].map(lambda x:MakeupMissing(x))
  • desc:将缺失作为一种状态,非缺失作为另外一种状态
    这里写图片描述
def DescExisting(x):x=str(x)if x=='nan':return 'no desc'else:return 'desc'data['desc_clean']=data['desc'].apply(DescExisting)
data.desc_clean.value_counts()
'''
desc       26818
no desc    12967
Name: desc_clean, dtype: int64
'''
2.1.3 标签处理和选择数据
'''
由于存在不同的贷款期限(term),申请评分卡模型评估的违约概率必须要在统一的期限中,且不宜太长,所以选取term=36months的行本
'''
data.term=data.term.apply(lambda x:int(x.replace(" months","")))
selectData=data.loc[data.term==36]
print (selectData.shape) #(29095, 32)data.loan_status.value_counts()
'''
Fully Paid     34115
Charged Off     5670
Name: loan_status, dtype: int64
'''
data['y']=data['loan_status'].map(lambda x:int(x=='Charged Off'))

2.2 特征衍生

#贷款额度与收入之比
selectData['amt_ratio']=selectData.apply(lambda x:x['loan_amnt']/x['annual_inc'],axis=1)#考虑earliest_cr_line到申请日期的跨度,以月份记
from dateutil.relativedelta import relativedelta
def MonthGap(earlyDate, lateDate):if lateDate > earlyDate:gap = relativedelta(lateDate,earlyDate)yr = gap.yearsmth = gap.monthsreturn yr*12+mthelse:return 0
selectData['earliest_cr_to_app'] = selectData.apply(lambda x: MonthGap(x.earliest_cr_line_clean,x.app_date_clean), axis = 1)

2.3 分箱

采用卡方分箱,要求分完后:

  • 不超过5箱
  • Bad Rate单调
  • 每箱同时包含好坏样本
  • 特殊值如-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','amt_ratio']
#类别型特征
cat_features = ['home_ownership', 'verification_status','desc_clean', 'purpose', 'zip_code','addr_state','pub_rec_bankruptcies_clean']

对于类别型变量:
1. 当取值较多时(>5),先用bad rate编码,再用连续型分箱的方式进行分箱
2. 当取值较少时:
(1) 如果每种类别同时包含好坏样本,无需分箱
(2) 如果有类别只包含好坏样本的一种,需要合并

more_value_features = []
less_value_features = []
# 第一步,检查类别型变量中,哪些变量取值超过5
for var in cat_features:valueCounts = len(set(trainData[var]))print ("{}:{}".format(var,valueCounts))if valueCounts > 5:more_value_features.append(var)  #取值超过5的变量,需要bad rate编码,再用卡方分箱法进行分箱else:less_value_features.append(var)print ("\nunique values for #{} >5, need chiMerge".format(" #".join(more_value_features)))
print ("unique values for #{} <=5".format(" #".join(less_value_features)))'''
home_ownership:5
verification_status:3
desc_clean:2
purpose:14
zip_code:776
addr_state:50
pub_rec_bankruptcies_clean:4unique values for #purpose #zip_code #addr_state >5, need chiMerge
unique values for #home_ownership #verification_status #desc_clean #pub_rec_bankruptcies_clean <=5
'''

当取值<5时:如果每种类别同时包含好坏样本,无需分箱;如果有类别只包含好坏样本的一种,需要合并(后续的WOE编码要求每组中包括好坏样本)。

下面定义一个计算样本在某一属性上的坏样本率的函数:

def BinBadRate(df, col, target, grantRateIndicator=0):''':param df: 需要计算好坏比率的数据集:param col: 需要计算好坏比率的特征:param target: 好坏标签:param grantRateIndicator: 1返回总体的坏样本率,0不返回:return: 每箱的坏样本率,以及总体的坏样本率(当grantRateIndicator==1时)'''total = df.groupby([col])[target].count()total = pd.DataFrame({'total': total})bad = df.groupby([col])[target].sum()bad = pd.DataFrame({'bad': bad})regroup = total.merge(bad, left_index=True, right_index=True, how='left')regroup.reset_index(level=0, inplace=True)regroup['bad_rate'] = regroup.apply(lambda x: x.bad * 1.0 / x.total, axis=1)dicts = dict(zip(regroup[col],regroup['bad_rate']))if grantRateIndicator==0:return (dicts, regroup)N = sum(regroup['total'])B = sum(regroup['bad'])overallRate = B * 1.0 / Nreturn (dicts, regroup, overallRate)

对于坏样本率为0或者1的组,需要进行合并:

def MergeBad0(df,col,target,direction='bad'):''':param df: 包含检验0%或者100%坏样本率:param col: 分箱后的变量或者类别型变量。检验其中是否有一组或者多组没有坏样本或者没有好样本。如果是,则需要进行合并:param target: 目标变量,0、1表示好、坏:return: 合并方案,使得每个组里同时包含好坏样本'''regroup=BinBadRate(df,col,target)[1]if direction=='bad':# 如果是合并0坏样本率的组,则跟最小的非0坏样本率的组进行合并regroup=regroup.sort_values(by='bad_rate')else:# 如果是合并0好样本样本率的组,则跟最小的非0好样本率的组进行合并regroup=regroup.sort_values(by='bad_rate',ascending=False)col_values=[[name] for name in regroup[col]] #属性的可能取值regroup.index=range(regroup.shape[0])for i in range(regroup.shape[0]-1):col_values[i+1]=col_values[i]+col_values[i+1]del_index.append(i)if direction=='bad':if regroup['bad_rate'][i+1]>0:breakelse: #direction=='good':if regroup['bad_rate'][i+1]<1:breaknew_col_values=[[name] for i, name in enumerate(col_values) if i not in del_index] #合并之后的取值集合newGroup={}for i in range(len(new_col_values)): for val in new_col_values[i]:newGroup[val]='Bin '+str(i)return newGroup # dict, key为属性的可能取值,value为对应的组
# (i)当取值<5时:如果每种类别同时包含好坏样本,无需分箱;如果有类别只包含好坏样本的一种,需要合并
merge_bin_dict = {}  #存放需要合并的变量,以及合并方法
var_bin_list = []   #由于某个取值没有好或者坏样本而需要合并的变量
for col in less_value_features:binBadRate = BinBadRate(trainData, col, 'y')[0]if min(binBadRate.values()) == 0 :  #由于某个取值没有坏样本而进行合并print ('{} need to be combined due to 0 bad rate'.format(col))combine_bin = MergeBad0(trainData, col, 'y')merge_bin_dict[col] = combine_binnewVar = col + '_Bin'trainData[newVar] = trainData[col].map(combine_bin)var_bin_list.append(newVar)if max(binBadRate.values()) == 1:    #由于某个取值没有好样本而进行合并print ('{} need to be combined due to 0 good rate'.format(col))combine_bin = MergeBad0(trainData, col, 'y',direction = 'good')merge_bin_dict[col] = combine_binnewVar = col + '_Bin'trainData[newVar] = trainData[col].map(combine_bin)var_bin_list.append(newVar)
# merge_bin_dict={'home_ownership': {'NONE': 'Bin 0', 'MORTGAGE': 'Bin 0', 'OWN': 'Bin 1', 'RENT': 'Bin 2', 'OTHER': 'Bin 3'}}
# var_bin_list=['home_ownership_bin']#less_value_features里剩下不需要合并的变量
less_value_features = [i for i in less_value_features if i + '_Bin' not in var_bin_list]
#less_value_features=['verification_status', 'desc_clean', 'pub_rec_bankruptcies_clean']

当类别型变量的取值大于5时,按bad rate进行编码

def BadRateEncoding(df, col, target):''':param df: :param col: 需要按照bad rate进行编码的特征,通常为类别型特征:param target: 类标:return: 属性的取值对应的bad rate'''regroup = BinBadRate(df, col, target, grantRateIndicator=0)[1]br_dict=regroup[[col,'bad_rate']].set_index([col]).to_dict()['bad_rate']badRateEnconding = df[col].map(lambda x: br_dict[x])return {'encoding':badRateEnconding, 'bad_rate':br_dict}
# (ii)当取值>5时:用bad rate进行编码,放入连续型变量里
br_encoding_dict = {}   #记录按照bad rate进行编码的变量,及编码方式
for col in more_value_features:br_encoding = BadRateEncoding(trainData, col, 'y')trainData[col+'_br_encoding'] = br_encoding['encoding']br_encoding_dict[col] = br_encoding['bad_rate']num_features.append(col+'_br_encoding')#br_encoding_dict:
# {'purpose': {'car': 0.08866995073891626, 'credit_card': 0.07595450852965069, 'debt_consolidation': 0.11088348271446863, 'educational': 0.16062176165803108, 'home_improvement': 0.08807045636509207, 'house': 0.10526315789473684, 'major_purchase': 0.08317399617590822, 'medical': 0.14426229508196722, 'moving': 0.14935064935064934, 'other': 0.13350923482849605, 'renewable_energy': 0.16279069767441862, 'small_business': 0.19866666666666666, 'vacation': 0.12560386473429952, 'wedding': 0.0794392523364486}, 'zip_code':{}}

参考资料

评分卡系列(二):特征工程
风险狗的数据分析之路

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/54354.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(信贷风控一)互联网金融业申请评分卡的介绍

互联网金融业申请评分卡的介绍 本文主要讲解以下知识点 信用违约风险的基本概念申请评分卡的重要性和特性贷款申请环节的数据介绍和描述非平衡样本问题的定义和解决方法 信用违约风险的基本概念 什么是信用违约风险&#xff1f; 交易对手未能履行约定契约中的义务而造成经…

运营商大数据:贷款客户获客:贷款行业客户怎么找

贷款行业竞争激烈&#xff0c;呈白热化状态。不论是通过线下还是线上&#xff0c;客户都是被电话营销骚扰的烦烦气气&#xff0c;是因为咱们这个行业的电销实在太猛了&#xff1b;你想找到你的客户&#xff0c;先找他们的聚集点。 在线下&#xff0c;银行、贷款机构、售楼部、…

信用贷款常见问题应对话术

1、你们的利息太高了 这个要看您跟什么贷款机构比了&#xff0c;如果您拿我们跟银行比&#xff0c;我们确实比银行高&#xff0c;但是我们的门槛要远远低于银行的要求&#xff0c;我们是无抵押无担保&#xff0c;而且办理也很简单&#xff0c;所以是没法跟银行比的&#xff1b;…

催付话术模板

开网店的商家肯定都遇过需要“催付”的客户&#xff0c;要么就是咨询未下单&#xff0c;要么就是下单未付款。如何将这部分消费者成功转化为已付款客户&#xff1f;下面小编就总结了20条常见的催付话术&#xff0c;可以随意翻牌哟&#xff0c;看是否能对大家有借鉴和帮助&#…

金融贷款行业如何高效获客,积累意向客户群体——运营商大数据

现如今贷款行业面对的运营压力日益扩大&#xff0c;顾客贮备是生存的关键&#xff0c;传统式的陌生拜访&#xff0c;一切随缘销售市场已不能满足其要求。互联网消费行为的融合与转变是在销售市场端反映&#xff0c;直接影响着广告推广广告策略的确立与运用。 可是&#xff0c;…

风控中英文术语手册(银行_消费金融信贷业务)_v3

金融风控术语字典&#xff08;中英文对照&#xff09; 1、风控系统部分 1.Blaze blaze是FICO公司产品&#xff0c;用于规则管理&#xff0c;是模型ABC卡开发的前身。信贷公司开始放贷时&#xff0c;数据量少&#xff0c;申请用户少&#xff0c;难以建立模型。因此前期一般会…

风控中英文术语手册(银行_消费金融信贷业务)

1、风控系统篇 1.Blaze blaze是FICO公司产品&#xff0c;用于规则管理&#xff0c;是模型ABC卡开发的前身。信贷公司开始放贷时&#xff0c;数据量少&#xff0c;申请用户少&#xff0c;难以建立模型。因此前期一般会用到专家经验判断好坏客户&#xff0c;然后通过风控决策管…

贷前风控流程与常见策略规则类型

编写&#xff1a;Joey 审核&#xff1a;Devin老师 在信贷领域工作当中&#xff0c;其实大多数公司的风控团队或相关模型数据团队&#xff0c;都是贷前服务工作更多一些。今天就以这贷前为例&#xff0c;一起探讨贷前需要重点去研究的相关策略规则。关注“金科应用研院”&#…

我们不会很快有GPT-5;让 ChatGPT 帮我们总结 Hacker News

&#x1f989; AI新闻 &#x1f680; OpenAI联合创始人Sam Altman&#xff1a;我们不会很快有GPT-5 摘要&#xff1a;在2023北京智源大会的“AI安全与对齐”主题论坛上&#xff0c;OpenAI联合创始人Sam Altman表示&#xff0c;目前他们没有答案&#xff0c;不会很快有GPT-5&a…

在IT行业饱和的情况下,2023年成为Android程序员还有发展前景吗?

都说IT行业不好了&#xff0c;说互联网如何内卷&#xff01; 但是你能找到哪个行业比IT好吗&#xff1f;比程序员赚的多&#xff0c;又不会被替代吗&#xff1f; 还有人说艺术家是最不会被替代的呢&#xff0c;你看ChatGPT出来&#xff0c;插画师是不是都失业了&#xff1f; …

ChatGPT 面对经济衰退,应该做些什么准备?

经济衰退是一个复杂的问题&#xff0c;它可能对个人和家庭造成很大的影响。为了应对经济衰退&#xff0c;建议采取以下措施&#xff1a; 储蓄&#xff1a;尽量多储存现金&#xff0c;以应对不确定的未来收入和开支。财务管理&#xff1a;控制开支&#xff0c;尽量减少不必要的…

SPSS常用的10种统计分析

目录 实验一 地理数据的统计处理 一、实验目的 二、实验内容 三、实验步骤 实验二 双变量相关分析 一、实验目的 二、实验内容 三、实验步骤 实验三 主成分分析 一、实验目的 二、实验内容 三、实验步骤 实验四 因子分析 一、实验目的 二、实验内容 三、实…

数据处理SPSS的数据类型分析

SPSS是一款数据统计与数据分析工具&#xff0c;操作简单属于数据分析的入门工具。 想要灵活使用SPSS&#xff0c;需要掌握两个方面内容&#xff1a;数据分析相关知识、SPSS操作 1 数据分析 在使用数据分析工具之前&#xff0c;首先要了解数据分析的思路&#xff0c;有的人刚拿…

SPSS(十二)SPSS对应分析(图文+数据集)

SPSS&#xff08;十二&#xff09;SPSS对应分析&#xff08;图文数据集&#xff09; 对应分析的介绍 对应分析其实是对分类变量进行信息浓缩的方法&#xff0c;之前的主成分分析/因子分析针对的是连续型的变量 分析分类变量间关系时 卡方检验只能给出总体有无关联的结论&am…

如何用SPSS进行数据分析?

1.什么是SPSS SPSS是社会统计科学软件包的简称&#xff0c; 其官方全称为IBM SPSS Statistics。SPSS软件包最初由SPSS Inc.于1968年推出&#xff0c;于2009年被IBM收购&#xff0c;主要运用于各领域数据的管理和统计分析。作为世界社会科学数据分析的标准&#xff0c;SPSS操作…

数据分析中的Excel、R、Python、SPSS、SAS和SQL

作为一直想入门数据分析的童鞋们来说&#xff0c;如何选定一门面向数据分析的编程语言或工具呢&#xff1f;注意是数据分析&#xff0c;而不是大数据哦&#xff0c;数据分析是基础了。 数据分析的工具千万种&#xff0c;综合起来万变不离其宗。无非是数据获取、数据存储、数据管…

SPSS数据分析-交叉表分析

交叉表(交叉列联表) 分析法是一种以表格的形式同时描述两个或多个变量的联合分布及其结果的统计分析方法&#xff0c;此表格反映了这些只有有限分类或取值的离散变量的联合分布。 当交叉表只涉及两个定类变量时&#xff0c;交叉表又叫做相依表。 交叉列联表分析易于理解&#x…

用SPSS做数据分析(1)

写在前面: 爬虫今天停更一天,今天是周六,今天要把最近网课学习SPSS课程做个总结和回顾,明天继续更新爬虫的内容,今天这篇文章主要是SPSS中的数据管理,一些关于数据的简单操作,是属于SPSS中比较基础的内容,希望看完博客能动手亲自实践一下,会有奇效 文章目录 写在前面:SPSS数据…

spss分析方法-T检验

t检验&#xff0c;也称student t检验&#xff08;Students t test&#xff09;&#xff0c;主要用于样本含量较小&#xff08;例如n < 30&#xff09;&#xff0c;总体标准差σ未知的正态分布。t检验是用t分布理论来推论差异发生的概率&#xff0c;从而比较两个平均数的差异…

用SPSS进行多变量数据分析

用SPSS进行多变量数据分析 1.将所给的数据输入SPSS 22.0中文版。分别设置变量为温度&#xff0c;体重1、2、3、4&#xff1b;体重&#xff0c;温度5、10、15、20、30。 2.用SPSS进行作图&#xff08;过程略&#xff09;。 3.对数据进行多因素变量分析&#xff0c;具体操作如…