来源:
https://blog.csdn.net/qq_41144446/article/details/107839967
https://zhuanlan.zhihu.com/p/354550808
https://blog.csdn.net/weixin_42060598/article/details/109856031
Python中信贷评分卡中常用的两个库有“scorecardpy”和“Toad”。其中scorecardpy是由谢士晨博士开发,该软件包是R软件包评分卡的python版本。它的目标是通过提供一些常见任务的功能,使传统信用风险计分卡模型的开发更加轻松有效。该包的功能及对应的函数如下:
· 数据划分(split_df)
· 变量选择(iv, var_filter)
· 变量按权重(woe)分箱(woebin,woebin_plot, woebin_adj, woebin_ply)
· 评分转换(scorecard, scorecard_ply)
· 模型评估(perf_eva,perf_psi)
说到评分卡建模工具,做过评分卡的应该都能想到谢博士的scorecardpy和专为工业界模型开发设计的Python工具包—Toad,两者相比,scorecardpy更加轻量级,且依赖较少,可以满足大多数场景下的评分卡构建。
为了使评分卡建模流程更加便捷,该Python包针对建模中各个关键步骤都提供了现成的函数,如下:
- 数据集划分 (split_df)
- 变量筛选(iv, var_filter)
- 变量分箱(woebin, woebin_plot, woebin_adj, woebin_ply)
- 分数转换(scorecard, scorecard_ply)
- 效果评估(perf_eva, perf_psi)
基于github主页上提供的案例,对每一步操作进行详细解读:
数据准备
import scorecardpy as sc # 加载德国信用卡相关数据集 dat = sc.germancredit()
得到的数据集情况如下:
变量筛选
然后根据变量的缺失率、IV值、同值性等因素,对20个变量进行筛选,并对目标变量进行标记,该数据集中creditability为目标变量:
dt_s = sc.var_filter(dat, y=”creditability”)
通过 var_filter() 函数筛选后,数据集保留以下13个变量:
默认的参数配置为:iv_limit=0.02, missing_limit=0.95, identical_limit=0.95,即当某个变量的 IV 值小于0.02,或缺失率大于95%,或同值率(除空值外)大于95%,则剔除掉该变量。
此外,该方法还内置了除上述以外的其他参数:
def var_filter(dt, y, x=None, iv_limit=0.02, missing_limit=0.95, identical_limit=0.95, var_rm=None, var_kp=None, return_rm_reason=False, positive=’bad|1’)
其中各参数含义如下:
- varrm可设置强制保留的变量,默认为空;
- varkp可设置强制剔除的变量,默认为空;
- return_rm_reason可设置是否返回剔除原因,默认为不返回(False);
positive可设置坏样本对应的值,默认为“bad|1”。
数据集划分
将数据集划分成训练集和测试集:
train, test = sc.split_df(dt_s, ‘creditability’).values()
split_df()函数包括以下参数:
def split_df(dt, y=None, ratio=0.7, seed=186)
默认的切分比例为7:3,也可自行修改参数ratio设置不同的切分比例。变量分箱
可通过woebin()函数对全部变量进行自动分箱,并基于woe_bin的结果,使用woebin_plot对各变量分箱的count distribution和bad probability进行可视化,可观察是否存在单调性:
bins = sc.woebin(dt_s, y=”creditability”) sc.woebin_plot(bins)
bins为字典形式,以变量名作为key,可获得单个变量的分箱结果详情:
woebin_plot()得到的可视化结果如下:
woebin()函数包括如下参数:
def woebin(dt, y, x=None, var_skip=None, breaks_list=None, special_values=None, stop_limit=0.1, count_distr_limit=0.05, bin_num_limit=8, # min_perc_fine_bin=0.02, min_perc_coarse_bin=0.05, max_num_bin=8, positive=”bad|1”, no_cores=None, print_step=0, method=”tree”, ignore_const_cols=True, ignore_datetime_cols=True, check_cate_num=True, replace_blank=True, save_breaks_list=None, **kwargs)
woebin()可针对数值型和类别型变量生成最优分箱结果,方法可选择决策树分箱、卡方分箱或自定义分箱。其他各参数的含义如下:var_skip: 设置需要跳过分箱操作的变量;
- breaks_list: 切分点列表,默认为空。如果非空,则按设置的切分点进行分箱处理;
- special_values: 设置需要单独分箱的值,默认为空;
- count_distr_limit: 设置分箱占比的最小值,一般可接受范围为0.01-0.2,默认值为0.05;
- stop_limit: 当IV值的增长率小于所设置的stop_limit,或卡方值小于qchisq(1-stoplimit, 1)时,停止分箱。一般可接受范围为0-0.5,默认值为0.1;
- bin_num_limit: 该参数为整数,代表最大分箱数。
- positive: 指定样本中正样本对应的标签,默认为”bad|1”;
- no_cores: 设置用于并行计算的 CPU 数目;
- print_step: 该参数为非负数,默认值为1。若print_step>0,每次迭代会输出变量名。若iteration=0或no_cores>1,不会输出任何信息;
- method: 设置分箱方法,可设置”tree”(决策树)或”chimerge”(卡方),默认值为”tree”;
- ignore_const_cols: 是否忽略常数列,默认值为True,即忽略常数列;
- ignore_datetime_cols: 是否忽略日期列,默认值为True,即忽略日期列;
- check_cate_num: 检查类别变量中枚举值数目是否大于50,默认值为True,即自动进行检查。若枚举值过多,会影响分箱过程的速度;
- replace_blank: 设置是否将空值填为None,默认为True。
若对自动分箱结果不满意,还可手动自定义分箱:
breaks_adj = { ‘age.in.years’: [26, 35, 40], ‘other.debtors.or.guarantors’: [“none”, “co-applicant%,%guarantor”] } bins_adj = sc.woebin(dt_s, y=”creditability”, breaks_list=breaks_adj)
于是年龄变量的分箱结果变成:
分箱完成后,使用woebin_ply()函数对变量进行woe变换,之后需要把所得到的woe值作为模型的输入:
train_woe = sc.woebin_ply(train, bins_adj) test_woe = sc.woebin_ply(test, bins_adj)
模型训练
ytrain = train_woe.loc[:,’creditability’] X_train = train_woe.loc[:,train_woe.columns != ‘creditability’] y_test = test_woe.loc[:,’creditability’] X_test = test_woe.loc[:,train_woe.columns != ‘creditability’] # 逻辑回归 ——— from sklearn.linear_model import LogisticRegression lr = LogisticRegression(penalty=’l1’, C=0.9, solver=’saga’, n_jobs=-1) lr.fit(X_train, y_train) lr.coef lr.intercept_ # 预测 train_pred = lr.predict_proba(X_train)[:,1] test_pred = lr.predict_proba(X_test)[:,1]
效果评估
可使用perf_eva()函数对模型效果进行计算及可视化,基于预测的概率值和label值,提供KS(kolmogorov-smirnow), ROC, lift以及precision-recall四种评估指标:
train_perf = sc.perf_eva(y_train, train_pred, title = “train”) test_perf = sc.perf_eva(y_test, test_pred, title = “test”)
该函数还包括以下参数:
def perf_eva(label, pred, title=None, groupnum=None, plot_type=[“ks”, “roc”], show_plot=True, positive=”bad|1”, seed=186)
参数plot_type可设置为:”ks”, “lift”, “roc”, “pr”,默认为[“ks”, “roc”]。
分数转换
然后基于’woebin’的结果和sklearn.linear_model的LogisticRegression,创建scorecard()函数,用于构建评分卡,只需一行代码:
card = sc.scorecard(bins_adj, lr, X_train.columns)
生成的结果为各变量名及其分箱、对应得分组成的字典,例如:
scorecard()包括以下参数:
def scorecard(bins, model, xcolumns, points0=600, odds0=1/19, pdo=50, basepoints_eq0=False, digits=0)
各参数含义如下:
- bins:由
woebin
得到的分箱信息; - model:LogisticRegression模型对象;
- points0:基准分数,默认值为600;
- odds0: 基准 Odds(好坏比),与真实违约概率对应,可换算得到违约概率,Odds = p/(1-p)。默认值为 1/19;
- pdo: Points toDouble theOdds,即Odds变成2倍时,所增加的信用分。默认值为50;
- basepoints_eq0:设置是否要把basepoints均分给每个变量的得分,默认为False,即不进行均分。但大多数评分卡倾向于所有分数均为正数,所以可手动改为True。
然后基于scorecard
的结果,用scorecard_ply()函数计算train和test数据集的信用分数:
train_score = sc.scorecard_ply(train, card, print_step=0) test_score = sc.scorecard_ply(test, card, print_step=0)
直接生成各样本的评分:
最后用perf_psi()得到该评分卡在测试数据集上的表现:
sc.perf_psi( score = {‘train’:train_score, ‘test’:test_score}, label = {‘train’:y_train, ‘test’:y_test} )
perf_psi()可计算群体稳定性指标(PSI),并提供信用分数的分布图:
参考资料
scorecardpy github主页:https://github.com/ShichenXie/s
# Traditional Credit Scoring Using Logistic Regression
# 基于逻辑回归的传统信用回归
import scorecardpy as sc
# data prepare ------
# 数据准备 -----
# load germancredit data
# 加载germancredit(德国信贷)数据
dat = sc.germancredit()
# filter variable via missing rate, iv, identical value rate
# 通过数据缺失率、IV值、相同值来过滤变量
dt_s = sc.var_filter(dat, y="creditability")
# breaking dt into train and test
# 将DataFrame分成训练集和测试集
train, test = sc.split_df(dt_s, 'creditability').values()
# woe binning ------
# 根据woe值进行分箱
bins = sc.woebin(dt_s, y="creditability")
# sc.woebin_plot(bins)
# binning adjustment
# 分箱调整
# # adjust breaks interactively
# # 有交互地进行重组调整
# breaks_adj = sc.woebin_adj(dt_s, "creditability", bins)
# # or specify breaks manually
# # 或者手动进行重组调整
breaks_adj = {
'age.in.years': [26, 35, 40],
'other.debtors.or.guarantors': ["none", "co-applicant%,%guarantor"]
}
bins_adj = sc.woebin(dt_s, y="creditability", breaks_list=breaks_adj)
# converting train and test into woe values
# 将测试集和训练集转换为woe值
train_woe = sc.woebin_ply(train, bins_adj)
test_woe = sc.woebin_ply(test, bins_adj)
y_train = train_woe.loc[:,'creditability']
X_train = train_woe.loc[:,train_woe.columns != 'creditability']
y_test = test_woe.loc[:,'creditability']
X_test = test_woe.loc[:,train_woe.columns != 'creditability']
# logistic regression ------
# 逻辑回归 -----
from sklearn.linear_model import LogisticRegression
lr = LogisticRegression(penalty='l1', C=0.9, solver='saga', n_jobs=-1)
lr.fit(X_train, y_train)
# lr.coef_
# lr.intercept_
# predicted proability
# 可能性预测
train_pred = lr.predict_proba(X_train)[:,1]
test_pred = lr.predict_proba(X_test)[:,1]
# performance ks & roc ------
# ks 和 roc 的性能表现 -----
train_perf = sc.perf_eva(y_train, train_pred, title = "train")
test_perf = sc.perf_eva(y_test, test_pred, title = "test")
# score ------
# 得分 -----
card = sc.scorecard(bins_adj, lr, X_train.columns)
# credit score
# 信用得分
train_score = sc.scorecard_ply(train, card, print_step=0)
test_score = sc.scorecard_ply(test, card, print_step=0)
# psi
#
sc.perf_psi(
score = {'train':train_score, 'test':test_score},
label = {'train':y_train, 'test':y_test}
)