利用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标准库之循环器(itertools)介绍
Nov 25 Python
Python的类实例属性访问规则探讨
Jan 30 Python
python学习笔记之调用eval函数出现invalid syntax错误问题
Oct 18 Python
详解Python中的__new__、__init__、__call__三个特殊方法
Jun 02 Python
Python如何实现文本转语音
Aug 08 Python
Python3实现对列表按元组指定列进行排序的方法分析
Dec 22 Python
简单易懂Pytorch实战实例VGG深度网络
Aug 27 Python
Python Print实现在输出中插入变量的例子
Dec 25 Python
TensorFlow——Checkpoint为模型添加检查点的实例
Jan 21 Python
浅谈keras的深度模型训练过程及结果记录方式
Jan 24 Python
python操作yaml说明
Apr 08 Python
5行Python代码实现图像分割的步骤详解
May 25 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
解析web文件操作常见安全漏洞(目录、文件名检测漏洞)
2013/06/29 PHP
php中删除、清空session的方式总结
2015/10/09 PHP
PHP+Mysql+Ajax实现淘宝客服或阿里旺旺聊天功能(前台页面)
2017/06/16 PHP
PHP编程实现微信企业向用户付款的方法示例
2017/07/26 PHP
php自定义排序uasort函数示例【二维数组按指定键值排序】
2019/06/19 PHP
用JavaScript计算在UTF-8下存储字符串占用字节数
2013/08/08 Javascript
node.js中的fs.mkdirSync方法使用说明
2014/12/17 Javascript
Javascript中With语句用法实例
2015/05/14 Javascript
纯js代码实现未知宽高的元素在指定元素中垂直水平居中显示
2015/09/12 Javascript
基于jQuery实现点击最后一行实现行自增效果的表格
2016/01/12 Javascript
详解Node.Js如何处理post数据
2016/09/19 Javascript
getElementById().innerHTML与getElementById().value的区别
2016/10/27 Javascript
js自定义Tab选项卡效果
2017/06/05 Javascript
JavaScript继承与多继承实例分析
2018/05/26 Javascript
Vue加载组件、动态加载组件的几种方式
2018/08/31 Javascript
vue中子组件传递数据给父组件的讲解
2019/01/27 Javascript
解决Vue动态加载本地图片问题
2019/10/09 Javascript
解决VUE mounted 钩子函数执行时 img 未加载导致页面布局的问题
2020/07/27 Javascript
原生js+css实现tab切换功能
2020/09/17 Javascript
python求pi的方法
2014/10/08 Python
python简单实现旋转图片的方法
2015/05/30 Python
Python使用Flask-SQLAlchemy连接数据库操作示例
2018/08/31 Python
python+mysql实现学生信息查询系统
2019/02/21 Python
Django 开发环境与生产环境的区分详解
2019/07/26 Python
pandas 强制类型转换 df.astype实例
2020/04/09 Python
python mock测试的示例
2020/10/19 Python
pandas apply使用多列计算生成新的列实现示例
2021/02/24 Python
Html5实现iPhone开机界面示例代码
2013/06/30 HTML / CSS
html5 canvas fillRect坐标和大小的问题解决方法
2014/03/26 HTML / CSS
简述Html5 IphoneX 适配方法
2018/02/08 HTML / CSS
广播体操比赛主持词
2015/06/29 职场文书
小学教师暑期培训心得体会
2016/01/09 职场文书
2016幼儿教师自荐信范文
2016/01/28 职场文书
SpringBoot生成License的实现示例
2021/06/16 Java/Android
java设计模式--七大原则详解
2021/07/21 Java/Android
《杜鹃的婚约》OP主题曲「凸凹」无字幕影像公开
2022/04/08 日漫