利用Python计算KS的实例详解


Posted in Python onMarch 03, 2020

在金融领域中,我们的y值和预测得到的违约概率刚好是两个分布未知的两个分布。好的信用风控模型一般从准确性、稳定性和可解释性来评估模型。

一般来说。好人样本的分布同坏人样本的分布应该是有很大不同的,KS正好是有效性指标中的区分能力指标:KS用于模型风险区分能力进行评估,KS指标衡量的是好坏样本累计分布之间的差值。

好坏样本累计差异越大,KS指标越大,那么模型的风险区分能力越强。

1、crosstab实现,计算ks的核心就是好坏人的累积概率分布,我们采用pandas.crosstab函数来计算累积概率分布。

2、roc_curve实现,sklearn库中的roc_curve函数计算roc和auc时,计算过程中已经得到好坏人的累积概率分布,同时我们利用sklearn.metrics.roc_curve来计算ks值

3、ks_2samp实现,调用stats.ks_2samp()函数来计算。链接scipy.stats.ks_2samp¶为ks_2samp()实现源码,这里实现了详细过程

4、直接调用stats.ks_2samp()计算ks

import pandas as pd 
import numpy as np
from sklearn.metrics import roc_curve
from scipy.stats import ks_2samp
 
def ks_calc_cross(data,pred,y_label):
  '''
  功能: 计算KS值,输出对应分割点和累计分布函数曲线图
  输入值:
  data: 二维数组或dataframe,包括模型得分和真实的标签
  pred: 一维数组或series,代表模型得分(一般为预测正类的概率)
  y_label: 一维数组或series,代表真实的标签({0,1}或{-1,1})
  输出值:
  'ks': KS值,'crossdens': 好坏客户累积概率分布以及其差值gap
  '''
  crossfreq = pd.crosstab(data[pred[0]],data[y_label[0]])
  crossdens = crossfreq.cumsum(axis=0) / crossfreq.sum()
  crossdens['gap'] = abs(crossdens[0] - crossdens[1])
  ks = crossdens[crossdens['gap'] == crossdens['gap'].max()]
  return ks,crossdens
 
def ks_calc_auc(data,pred,y_label):
  '''
  功能: 计算KS值,输出对应分割点和累计分布函数曲线图
  输入值:
  data: 二维数组或dataframe,包括模型得分和真实的标签
  pred: 一维数组或series,代表模型得分(一般为预测正类的概率)
  y_label: 一维数组或series,代表真实的标签({0,1}或{-1,1})
  输出值:
  'ks': KS值
  '''
  fpr,tpr,thresholds= roc_curve(data[y_label[0]],data[pred[0]])
  ks = max(tpr-fpr)
  return ks
 
def ks_calc_2samp(data,pred,y_label):
  '''
  功能: 计算KS值,输出对应分割点和累计分布函数曲线图
  输入值:
  data: 二维数组或dataframe,包括模型得分和真实的标签
  pred: 一维数组或series,代表模型得分(一般为预测正类的概率)
  y_label: 一维数组或series,代表真实的标签({0,1}或{-1,1})
  输出值:
  'ks': KS值,'cdf_df': 好坏客户累积概率分布以及其差值gap
  '''
  Bad = data.loc[data[y_label[0]]==1,pred[0]]
  Good = data.loc[data[y_label[0]]==0, pred[0]]
  data1 = Bad.values
  data2 = Good.values
  n1 = data1.shape[0]
  n2 = data2.shape[0]
  data1 = np.sort(data1)
  data2 = np.sort(data2)
  data_all = np.concatenate([data1,data2])
  cdf1 = np.searchsorted(data1,data_all,side='right')/(1.0*n1)
  cdf2 = (np.searchsorted(data2,data_all,side='right'))/(1.0*n2)
  ks = np.max(np.absolute(cdf1-cdf2))
  cdf1_df = pd.DataFrame(cdf1)
  cdf2_df = pd.DataFrame(cdf2)
  cdf_df = pd.concat([cdf1_df,cdf2_df],axis = 1)
  cdf_df.columns = ['cdf_Bad','cdf_Good']
  cdf_df['gap'] = cdf_df['cdf_Bad']-cdf_df['cdf_Good']
  return ks,cdf_df
 
data = {'y_label':[1,1,1,1,1,1,0,0,0,0,0,0],
    'pred':[0.5,0.6,0.7,0.6,0.6,0.8,0.4,0.2,0.1,0.4,0.3,0.9]}
 
data = pd.DataFrame(data)
ks1,crossdens=ks_calc_cross(data,['pred'], ['y_label'])
 
ks2=ks_calc_auc(data,['pred'], ['y_label'])
 
ks3=ks_calc_2samp(data,['pred'], ['y_label'])
 
get_ks = lambda y_pred,y_true: ks_2samp(y_pred[y_true==1], y_pred[y_true!=1]).statistic
ks4=get_ks(data['pred'],data['y_label'])
print('KS1:',ks1['gap'].values)
print('KS2:',ks2)
print('KS3:',ks3[0])
print('KS4:',ks4)

输出结果:

KS1: [ 0.83333333]
KS2: 0.833333333333
KS3: 0.833333333333
KS4: 0.833333333333

当数据中存在NAN数据时,有一些问题需要注意!

例如,我们在原数据中增加了y_label=0,pred=np.nan这样一组数据

data = {'y_label':[1,1,1,1,1,1,0,0,0,0,0,0,0],
'pred':[0.5,0.6,0.7,0.6,0.6,0.8,0.4,0.2,0.1,0.4,0.3,0.9,np.nan]}

此时执行

ks1,crossdens=ks_calc_cross(data,['pred'], ['y_label'])

输出结果

KS1: [ 0.83333333]

执行

ks2=ks_calc_auc(data,['pred'], ['y_label'])

将会报以下错误

ValueError: Input contains NaN, infinity or a value too large for dtype('float64').

执行

ks3=ks_calc_2samp(data,['pred'], ['y_label'])

输出结果

KS3: 0.714285714286

执行

ks4=get_ks(data['pred'],data['y_label'])

输出结果

KS4: 0.714285714286

我们从上述结果中可以看出

三种方法计算得到的ks值均不相同。

ks_calc_cross计算时忽略了NAN,计算得到了数据正确的概率分布,计算的ks与我们手算的ks相同

ks_calc_auc函数由于内置函数无法处理NAN值,直接报错了,所以如果需要ks_calc_auc计算ks值时,需要提前去除NAN值。

ks_calc_2samp计算得到的ks因为searchsorted()函数(有兴趣的同学可以自己模拟数据看下这个函数),会将Nan值默认排序为最大值,从而改变了数据的原始累积分布概率,导致计算得到的ks和真实的ks有误差。

总结

在实际情况下,我们一般计算违约概率的ks值,这时是不存在NAN值的。所以以上三种方法计算ks值均可。但是当我们计算单变量的ks值时,有时数据质量不好,存在NAN值时,继续采用ks_calc_auc和ks_calc_2samp就会存在问题。

解决办法有两个

1. 提前去除数据中的NAN值

2. 直接采用ks_calc_cross计算。

以上这篇利用Python计算KS的实例详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
用python删除java文件头上版权信息的方法
Jul 31 Python
python版本的读写锁操作方法
Apr 25 Python
python 采集中文乱码问题的完美解决方法
Sep 27 Python
python获取当前用户的主目录路径方法(推荐)
Jan 12 Python
Python matplotlib 画图窗口显示到gui或者控制台的实例
May 24 Python
Python实现高斯函数的三维显示方法
Dec 29 Python
pyqt5实现绘制ui,列表窗口,滚动窗口显示图片的方法
Jun 20 Python
python移位运算的实现
Jul 15 Python
Django REST framework内置路由用法
Jul 26 Python
Python获取统计自己的qq群成员信息的方法
Nov 15 Python
python pyenv多版本管理工具的使用
Dec 23 Python
Pycharm中Python环境配置常见问题解析
Jan 16 Python
python如何提取英语pdf内容并翻译
Mar 03 #Python
Pycharm如何运行.py文件的方法步骤
Mar 03 #Python
python生成大写32位uuid代码
Mar 03 #Python
python str字符串转uuid实例
Mar 03 #Python
PyCharm取消波浪线、下划线和中划线的实现
Mar 03 #Python
python生成并处理uuid的实现方式
Mar 03 #Python
python实现在线翻译功能
Mar 03 #Python
You might like
使用sockets:从新闻组中获取文章(二)
2006/10/09 PHP
rrmdir php中递归删除目录及目录下的文件
2011/05/15 PHP
Alliance vs Liquid BO3 第二场2.13
2021/03/10 DOTA
对联广告js flash激活
2006/10/19 Javascript
jValidate 基于jQuery的表单验证插件
2009/12/12 Javascript
地址栏传递中文参数乱码在js里用escape转码
2013/08/28 Javascript
js实现弹出窗口、页面变成灰色并不可操作的例子分享
2014/05/10 Javascript
深入探讨javascript中的数据类型
2015/03/04 Javascript
JavaScript中匿名函数用法实例
2015/03/23 Javascript
Bootstrap进度条组件知识详解
2016/05/01 Javascript
JQuery学习总结【一】
2016/12/01 Javascript
layer弹出层中H5播放器全屏出错的解决方法
2017/02/21 Javascript
基于Vue制作组织架构树组件
2017/12/06 Javascript
JavaScript实现获取select下拉框中第一个值的方法
2018/02/06 Javascript
vue-router的使用方法及含参数的配置方法
2018/11/13 Javascript
Vue开发之封装分页组件与使用示例
2019/04/25 Javascript
原生js实现针对Dom节点的CRUD操作示例
2019/08/26 Javascript
vue简单练习 桌面时钟的实现代码实例
2019/09/19 Javascript
jquery实现弹窗(系统提示框)效果
2019/12/10 jQuery
vue 如何从单页应用改造成多页应用
2020/10/23 Javascript
[02:44]重置世界,颠覆未来——DOTA2 7.23版本震撼上线
2019/12/01 DOTA
[22:07]DOTA2-DPC中国联赛 正赛 iG vs Magma 选手采访
2021/03/11 DOTA
Python基于回溯法子集树模板解决m着色问题示例
2017/09/07 Python
python实现微信自动回复功能
2018/04/11 Python
Python3 Post登录并且保存cookie登录其他页面的方法
2018/12/28 Python
scrapy框架携带cookie访问淘宝购物车功能的实现代码
2020/07/07 Python
Web前端绘制0.5像素的几种方法
2017/08/11 HTML / CSS
挪威太阳镜和眼镜网上商城:SmartBuyGlasses挪威
2016/08/20 全球购物
GANT葡萄牙官方商店:拥有美国运动服传统的生活方式品牌
2018/10/18 全球购物
如何防止同一个帐户被多人同时登录
2013/08/01 面试题
秋季运动会加油稿200字
2014/01/11 职场文书
机械制造专业毕业生求职信
2014/03/02 职场文书
情感电台广播稿
2015/08/18 职场文书
多属性、多分类MySQL模式设计
2021/04/05 MySQL
Python+腾讯云服务器实现每日自动健康打卡
2021/12/06 Python
Win10服务主机占用内存怎么办?Win10服务主机进程占用大量内存解决方法
2022/09/23 数码科技