sklearn-逻辑回归-制作评分卡

目录

数据集处理

分箱

分多少个箱子合适

分箱要达成什么样的效果

对一个特征进行分箱的步骤

分箱的实现

封装计算 WOE 值和 IV值函数

画IV曲线,判断最佳分箱数量

结论

pd.qcut 执行报错

功能函数封装

判断分箱个数


在银行借贷场景中,评分卡是一种以分数形式来衡量一个客户的信用风险大小的手段,它衡量向别人借钱的人(受信人,需要融资的公司)不能如期履行合同中的还本付息责任,并让借钱给别人的人(授信人,银行)造成经济损失的可能性。一般来说,评分卡打出的分值越高,客户的信用越好,风险越小。

数据集处理

分箱


要制作评分卡,是要给各个特征进行分档,以便业务人员能够根据新客户填写的信息,为这个新客户来打分。因此在评分卡制作过程中,一个重要的步骤就是分箱,本质就是对特征进行分档。

分箱是评分卡最难,也是最核心的部分。分箱的本质,就是离散化连续变量,好让拥有不同属性的人被分成不同的类别(打上不同的分数)。

分多少个箱子合适

既然是将连续型变量离散化,箱子的个数必然不能太多,最好控制在十个以下,用来制作评分卡,最好能在4~5个为最佳。离散化连续变量必然伴随着信息的损失,而且箱子越少,信息损失越大。
为了衡量特征上的信息量以及特征对预测函数的贡献,银行业定义了概念Information value(IV):
 

  •  N 是这个特征上箱子的个数
  •  i 代表每个箱子
  •  good% 是这个箱内的优质客户(标签为0)占整个特征中所有优质客户的比例
  •  bad% 是这个箱子里的坏客户(那些会违约的,标签为1)占整个特征中所有坏客户的比例
  •  WOE 是银行业中用来衡量违约概率的指标,中文叫做证据权重(weight of Evidence),本质就是优质客户比上坏客户的比例的对数,WOEi写作 

WOE是对一个箱子来说的,WOE越大,代表这个箱子里的优质客户越多,IV是对整个特征来说的,IV代表的意义由 表1 来控制

表1:

可见,IV 并非越大越好,我们想要找到 IV 的大小和箱子个数的平衡点,所以我们会对特征进行分箱,然后计算每个特征在每个箱子数目下的WOE值,利用IV值的曲线,找出合适的分箱个数。

分箱要达成什么样的效果

我们希望在同一个箱子里的人的属性是尽量相似的,而不同箱子里的人的属性是尽量不同的,就是常说的“组间差异大,组内差异小”。
对于评分卡来说,我们希望一个箱子内的人违约概率是类似的,而不同箱子的人违约概率差距很大,即 WOE 差距要大,并且每个箱子中坏客户所占的比重(bad%)也要不同。
我们可以使用卡方检验来对比两个箱子之间的相似性,如果两个箱子之间卡方检验的P值很大,说明他们非常相似,就可以将这两个箱子合并为一个箱子。

对一个特征进行分箱的步骤

  1. 首先把连续型变量分成一组数量较多的分类型变量,比如,将几万个样本分成100组或者50组
  2. 确保每一组中都要包含两种类别的样本,否则IV值会无法计算
  3. 对相邻的组进行卡方检验,卡方检验的P值很大的组进行合并,直到数据中的组数小于设定的N箱为止
  4. 我们让一个特征分别分成[2,3,4...20]箱,观察每个分箱个数下的IV值如何变化,找出最适合的分箱个数
  5. 分箱完毕后,我们计算每个箱的WOE值,bad%,观察分箱效果

这些步骤都完成后,我们可以对各个特征都进行分箱,然后观察每个特征的IV值,以此来挑选特征。

分箱的实现

封装计算 WOE 值和 IV值函数

# 计算 WOE 和 BAD RATE
# BAD RATE 是一个箱中,坏的样本所占的比例
# bad% 是一个箱中的坏样本占整个特征中的坏样本的比例def get_woe(num_bins):# 通过 num_bins 数据计算 woecolumns = ["min", "max", "count_0", "count_1"]dataf = pd.DataFrame(num_bins, columns=columns)# 一个箱子中所有的样本数dataf["total"] = dataf.count_0 + dataf.count_1# 一个箱子里的样本数,占所有样本数的比例dataf["percentage"] = dataf.total / dataf.total.sum()dataf["bad_rate"] = dataf.count_1 / dataf.totaldataf["good%"] = dataf.count_0 / dataf.count_0.sum()dataf["bad%"] = dataf.count_1/dataf.count_0.sum()dataf["woe"] = np.log(dataf["good%"] / dataf["bad%"])return dataf# 计算 IV 值
def get_iv(bins_df):rate = bins_df["good%"] - bins_df["bad%"]iv = np.sum(rate * bins_df.woe)return iv

画IV曲线,判断最佳分箱数量

# 导入数据
df = mlp.get_data_source("data8770d35cf63711ee80850242ac850002", return_type="dataframe")
# 删除不用于逻辑回归的列,只有数字类型的列才参与逻辑回归
# axis=1为删除列,第一个参数则输入表头的列表
# axis=0为删除行,第一个参数则输入索引的列表
df.drop(["apply_no", "pt", "query_no", "request_hash"], inplace=True, axis=1)
# 通过查看标签值的分布,发现1的个数只有80个,占比只有1%左右,属于严重不均衡的。如 图1 所示
print("标签值y分布情况:\n{}".format(y.value_counts()))
print("标签值y取1的占比情况:\n{}".format(y.value_counts()[1]/X.shape[0]))# 查看数据分布情况,图2 所示
df.describe([0.01,0.1,0.25,.5,.75,.9,.99]).TX = pd.DataFrame(X)
y = pd.DataFrame(y)# 分训练集和测试集,训练集用来建模,验证数据用来检测模型的效果的
Xtrain, Xtest, Ytrain, Ytest = train_test_split(X, y, test_size = 0.3, random_state=420)
# 逻辑回归做评分卡时,建模数据需要将训练集的特征矩阵和标签要合并在一起
# 因为分箱的时候,需要的是特征矩阵+标签的结构
model_data = pd.concat([Ytrain, Xtrain], axis=1)
# 更新索引
model_data.index = range(model_data.shape[0])# 按照等频对需要分箱的列进行分箱,保证每个箱子中好的样本数量和坏的样本数据都 > 0,如果有0,需要减少分箱数量
# "sq_model_score_high" 为例
model_data["qcut"], updown = pd.qcut(model_data["sq_model_score_high"], retbins=True, q=10, duplicates='drop')"""
pd.qcut,基于分位数的分箱函数,本质是将连续型变量离散化,只能够处理一维数据
返回箱子的上限和下限
参数q: 要分箱的个数
参数 retbins=True来要求除了返回箱子的上 限和下限,同时返回一个为索引为样本的索引、元素为分到的箱子的Series的结构
现在返回两个值:每个样本属于哪个箱子(赋值给model_data["qcut"]列),以及所有箱子的上限和下限(赋值给updown变量)
"""
# 查看分箱情况,如 图3 所示
print("分箱结束后,每个箱子的元素个数:\n{}".format(model_data["qcut"].value_counts()))# 统计每个分段 0, 1 的数,结果如 图4 所示
# 这里使用了数据透视表的功能 groupby
count_y0 = model_data[model_data["y"] == 0].groupby(by="qcut").count()["y"]
count_y1 = model_data[model_data["y"] == 1].groupby(by="qcut").count()["y"]print("对列 sq_model_score_high 进行分箱后,每个箱子对应的0的数量为:\n{}".format(count_y0))
print("对列 sq_model_score_high 进行分箱后,每个箱子对应的1的数量为: \n{}".format(count_y1))# num_bins 值分别为每个区间的上界、下届,0 出现的次数,1 出现的次数
num_bins = [*zip(updown, updown[1:], count_y0, count_y1)]# 卡方检验num_bins_ = num_bins.copy()
import matplotlib.pyplot as plt
import scipy
IV = []
axisx = []
# 定义箱的数量,这里为2
N = 2while len(num_bins_) > N:pvs = []# 获取 num_bins_两两之间的卡方检验的置信度(卡方值)for i in range(len(num_bins_)-1):x1 = num_bins_[i][2:]x2 = num_bins_[i+1][2:]# scipy.stats.chi2_contingency()0索引返回 chi2 值,1索引返回 p 值pv = scipy.stats.chi2_contingency([x1, x2])[1]pvs.append(pv)# 通过 p 值进行处理,合并 p 值最大的两组# 找出 p 值最大的那一组所在的下标 i,num_bins_的[i]和[i+1]就是可以合并的组i = pvs.index(max(pvs))# 具体合并第i个箱子和i+1个箱子操作,把 num_bins_的[i:i+2]左闭右开换成新列表[()],新列表元素内容按照新方式计算# 1、第0个元素取第i个箱子的第0个元素,前一个箱子的下限# 2、第1个元素取第i+1个箱子的第1个元素,后一个箱子的上限# 3、第2个元素取第i个箱子的第2个元素和第i+1个箱子的第2个元素加和# 4、第3个元素取第i个箱子的第3个元素和第i+1个箱子的第3个元素加和num_bins_[i:i+2] = [(num_bins_[i][0],num_bins_[i+1][1],num_bins_[i][2]+num_bins_[i+1][2],num_bins_[i][3]+num_bins_[i+1][3])]bins_df = get_woe(num_bins_)# 记录箱子数axisx.append(len(num_bins_))# 记录对应的IV值IV.append(get_iv(bins_df))plt.figure()
plt.plot(axisx, IV)
plt.xticks(axisx)
# y 坐标为 IV值
plt.ylabel("IV")
# x 坐标为箱子数量
plt.xlabel("N")
# 画出 IV 值随着箱子数量的变化而变化图,如 图5 所示
plt.show()

图1:

图2:

图3:

图4:

图5:

结论

如 图5 所示,分箱数量越多,IV值越高,在这条线中,寻找随着箱子数量减少,IV值下降最快的那个点,这个转折点对应的箱子数量就是我们要找的相对合适的箱子数量。
这样,就这出了特征 sq_model_score_high 的最佳分箱个数

pd.qcut 执行报错

图6:

如 图6 所示,运行 pd.qcut()函数的时候,如果出现"ValueError: Bin edges must be unique"的错误,这通常意味着在尝试对数据进行分箱时,边界值出现了重复。这可能会导致qcut函数无法确定如何对数据进行分箱,因此需要进行调整以确保边界值唯一。
为了解决这个问题,可以在调用qcut()函数时,传入duplicates='drop'参数来指定处理重复边界值的方式,选择将重复的边界值删除,这可能会导致最终分箱后的箱的数量减少,以及数据条数减少;或者使用cut()函数,pd.cut()函数是根据值本身来确定分箱的边界,因此可以处理重复的边界值,并将它们归入相邻的箱中

model_data["cut"], updown = pd.cut(model_data["sq_model_score_high"], retbins=True, bins=20)

功能函数封装

判断分箱个数
 

def graph_for_best_bin(DF, X, Y, N=5, q=20, graph=True):"""自动优化分箱函数,基于卡方验证的分箱DF:需要输入的数据X:需要分箱的列名Y:分箱数据对应的标签 Y 列名N:保留分箱个数q: 初始分箱的个数graph:是否要画出IV图像区间为前开后闭(]"""import matplotlib.pyplot as pltimport scipymodel_data = DF[[X,Y]].copy()model_data["qcut"], updown = pd.qcut(model_data[X], retbins=True, q=q, duplicates='drop')count_y0 = model_data.loc[model_data[Y] == 0].groupby(by="qcut").count()[Y]count_y1 = model_data.loc[model_data[Y] == 1].groupby(by="qcut").count()[Y]num_bins_ = [*zip(updown, updown[1:], count_y0, count_y1)]for i in range(q):if 0 in num_bins_[0][2:]:num_bins_[0:2] = [(num_bins_[0][0],num_bins_[1][1],num_bins_[0][2]+num_bins_[1][2],num_bins_[0][3]+num_bins_[1][3])]for i in range(len(num_bins_)):if 0 in num_bins_[i][2:]:num_bins_[i-1:i+1] = [(num_bins_[i-1][0],num_bins_[i][1],num_bins_[i-1][2]+num_bins_[i][2],num_bins_[i-1][3]+num_bins_[i][3])]breakelse:breakdef get_woe(num_bins):columns = ["min", "max", "count_0", "count_1"]dataf = pd.DataFrame(num_bins, columns=columns)dataf["total"] = dataf.count_0 + dataf.count_1dataf["percentage"] = dataf.total / dataf.total.sum()dataf["bad_rate"] = dataf.count_1 / dataf.totaldataf["good%"] = dataf.count_0 / dataf.count_0.sum()dataf["bad%"] = dataf.count_1/dataf.count_1.sum()dataf["woe"] = np.log(dataf["good%"] / dataf["bad%"])return dataf# 计算 IV 值def get_iv(bins_df):rate = bins_df["good%"] - bins_df["bad%"]iv = np.sum(rate * bins_df.woe)return ivIV = []axisx = []while len(num_bins_) > N:pvs = []for i in range(len(num_bins_)-1):x1 = num_bins_[i][2:]x2 = num_bins_[i+1][2:]pv = scipy.stats.chi2_contingency([x1, x2])[1]pvs.append(pv)i = pvs.index(max(pvs))num_bins_[i:i+2] = [(num_bins_[i][0],num_bins_[i+1][1],num_bins_[i][2]+num_bins_[i+1][2],num_bins_[i][3]+num_bins_[i+1][3])]bins_df = get_woe(num_bins_)# 记录箱子数axisx.append(len(num_bins_))# 记录对应的IV值IV.append(get_iv(bins_df))if graph:plt.figure()plt.plot(axisx, IV)plt.xticks(axisx)plt.ylabel("IV")plt.xlabel("N")plt.show()return bins_df

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

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

相关文章

Http请求响应——请求

Http概述 Http协议(HyperText Transfer Protocol,超文本传输协议),是一种用于传输网页数据的协议,规定了浏览器和服务器之间进行数据传输的规则,简单说来就是客户端与服务器端数据交互的数据格式。 客户端…

python学opencv|读取图像(三十一)缩放图像的三种方法

【1】引言 前序学习进程中,我们至少掌握了两种方法,可以实现对图像实现缩放。 第一种方法是调用cv2.resize()函数实现,相关学习链接为: python学opencv|读取图像(三)放大和缩小图像_python opencv 读取图…

封装红黑树实现map和set

本博客需要红黑树和搜索树二叉树的一些知识以及熟悉map和set的相关函数和迭代器,如果读者还不熟悉可以看这三篇博客:红黑树、二叉搜索树、map、set的使用 红黑树的封装 STL30源码分析 如果想到封装,大家应该会直接把RBtree复制两份&#x…

关于使用FastGPT 摸索的QA

近期在通过fastGPT,创建一些基于特定业务场景的、相对复杂的Agent智能体应用。 工作流在AI模型的基础上,可以定义业务逻辑,满足输出对话之外的需求。 在最近3个月来的摸索和实践中,一些基于经验的小问题点(自己也常常…

LeetCode 热题 100_二叉树的最近公共祖先(48_236_中等_C++)(二叉树;深度优先搜索)

LeetCode 热题 100_二叉树的最近公共祖先(48_236) 题目描述:输入输出样例:题解:解题思路:思路一(深度优先搜索): 代码实现代码实现(思路一(深度优…

HTTP/HTTPS ②-Cookie || Session || HTTP报头

这里是Themberfue 上篇文章介绍了HTTP报头的首行信息 本篇我们将更进一步讲解HTTP报头键值对的含义~~~ ❤️❤️❤️❤️ 报头Header ✨再上一篇的学习中,我们了解了HTTP的报头主要是通过键值对的结构存储和表达信息的;我们已经了解了首行的HTTP方法和UR…

PyCharm+RobotFramework框架实现UDS自动化测试——(二)RobotFramework环境配置

从0开始学习CANoe使用 从0开始学习车载测试 相信时间的力量 星光不负赶路者,时光不负有心人。 文章目录 1.环境准配2.Pycharm中相关配置2.1. 安装Hyper RobotFramework Support 3.脚本执行环境3.1 执行单条的配置3.2 执行全部用例配置 4.工程运行4.1 单条用例运行4.…

Android原生开发同一局域网内利用socket通信进行数据传输

1、数据接收端代码如下,注意:socket 接收信息需要异步运行: // port 端口号自定义一个值,比如 8888,但需和发送端使用的端口号保持一致 ServerSocket serverSocket new ServerSocket(port); while (true) {//这里为了…

腾讯云AI代码助手编程挑战赛-算法小助手

作品简介 一个可以帮助学习计算机各种算法的AI小助手,提升工作效率。 技术架构 使用Html语言完成图形化页面的样式,使用JavaScript语言来操作对应的逻辑代码。 实现过程 1、创建一个界面 2、获取数据 3、添加按钮与功能 4、程序优化调试 开发环境…

使用 IntelliJ IDEA 创建简单的 Java Web 项目

以下是使用 IntelliJ IDEA 创建几个简单的 Java Web 项目的步骤,每个项目实现基本的登录、注册和查看列表功能,依赖 Servlet/JSP 和基本的 Java Web 开发。 前置准备 确保安装了 IntelliJ IDEA Ultimate(社区版不支持 Web 应用)。…

抓包工具之mitmproxy

一、mitmproxy简介 mitmproxy介绍 mitmproxy又名中间人攻击代理,是一个抓包工具,类似于WireShark、Filddler,并且它支持抓取HTTP和HTTPS协议的数据包,只不过它是一个控制台的形式操作。另外,它还有两个非常有用的组件…

Flutter项目开发模版,开箱即用(Plus版本)

前言 当前案例 Flutter SDK版本:3.22.2 本文,是由这两篇文章 结合产出,所以非常建议大家,先看完这两篇: Flutter项目开发模版: 主要内容:MVVM设计模式及内存泄漏处理,涉及Model、…

rk3568 , buildroot , qt ,使用sqlite, 动态库, 静态库

问题说明: 客户反馈 ,buildroot 系统 ,使用qt 使用sqlite ,有报错,无法使用sqlite. 测试情况说明: 我自己测试,发现, buildroot 自己默认就是 使能了 sqlite 的。 是否解决说明&…

投机解码论文阅读:Falcon

题目:Falcon: Faster and Parallel Inference of Large Language Models through Enhanced Semi-Autoregressive Drafting and Custom-Designed Decoding Tree 地址:https://arxiv.org/pdf/2412.12639 一看它的架构图,可以发现它是基于EAGLE…

鸿蒙UI(ArkUI-方舟UI框架)

参考:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V13/arkts-layout-development-overview-V13 ArkUI简介 ArkUI(方舟UI框架)为应用的UI开发提供了完整的基础设施,包括简洁的UI语法、丰富的UI功能&#xff…

TensorFlow Quantum快速编程(基本篇)

一、TensorFlow Quantum 概述 1.1 简介 TensorFlow Quantum(TFQ)是由 Google 开发的一款具有开创性意义的开源库,它宛如一座桥梁,巧妙地将量子计算与 TensorFlow 强大的机器学习功能紧密融合。在当今科技飞速发展的时代,传统机器学习虽已取得诸多瞩目成就,然而面对日益…

Qt天气预报系统获取天气数据

Qt天气预报系统获取天气数据 1、获取天气数据1.1添加天气类头文件1.2定义今天和未来几天天气数据类1.3定义一个解析JSON数据的函数1.4在mainwindow中添加weatherData.h1.5创建今天天气数据和未来几天天气数据对象1.6添加parseJson定义1.7把解析JSON数据添加进去1.8添加错误1.9解…

国产编辑器EverEdit - 扩展脚本:关闭所有未修改文档

1 扩展脚本:关闭所有未修改文档 1.1 应用场景 当用户打开过多文档时,部分文档已经修改,而大部分没有修改,为了减少在众多已打开文档中来回跳转的不便,可以将没有修改的文档全部关闭,但目前提供的快速关闭窗…

高斯函数Gaussian绘制matlab

高斯 约翰卡尔弗里德里希高斯,(德语:Johann Carl Friedrich Gau,英语:Gauss,拉丁语:Carolus Fridericus Gauss)1777年4月30日–1855年2月23日,德国著名数学家、物理学家…

dolphinscheduler2.0.9升级3.1.9版本问题记录

相关版本说明 JDK:JDK (1.8) DolphinScheduler :3.1.9 数据库:MySQL (8),驱动:MySQL JDBC Driver 8.0.16 注册中心:ZooKeeper (3.8.4) 问题一:dolphinscheduler2.0.9对应zk版本使用…