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,一经查实,立即删除!

相关文章

【人工智能】自然语言生成的前沿探索:利用GPT-2和BERT实现自动文本生成与完形填空

自然语言生成(Natural Language Generation, NLG)是人工智能领域的重要研究方向,旨在通过计算机系统自动生成连贯、符合语法和语义的自然语言文本。近年来,预训练语言模型如GPT-2和BERT在NLG任务中取得了显著的成果。本文深入探讨…

OpenCV相机标定与3D重建(53)解决 Perspective-3-Point (P3P) 问题函数solveP3P()的使用

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 根据 3 个 3D-2D 点对应关系找到物体的姿态。 cv::solveP3P 是 OpenCV 中的一个函数,用于解决 Perspective-3-Point (P3P) 问题。该问…

PHP语言的软件工程

PHP语言的软件工程 引言 在当今数字化时代,网络应用的需求与日俱增,而PHP语言作为一种广泛使用的服务器端脚本语言,凭借其简单易学、高效灵活,成为了Web开发领域的重要工具之一。本文将探讨PHP语言在软件工程中的应用&#xff0…

shell脚本练习(3)

一、编写一个shell脚本,功能如下: (1)提示用户输入网络接口名称。 (2)根据接口返回IP。 [rootopenEuler-1 ~]# cat showIP.sh #!/bin/bash# 获取接口名 net_nameip a | awk -F"[ :]" /^[0-9]:/ …

Http请求响应——请求

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

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

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

windows中,git bash 使用conda命令

1、首先在Anaconda的安装路径如/Anaconda3/Scripts下,打开git bash窗口,然后输入下面的命令。 ./conda init bash 运行之后,会在用户目录下面生成.bash_profile文件,文件内容如下: # >>> conda initialize…

封装红黑树实现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.…

git 转移文件夹

打开终端或命令行界面:首先,确保你的电脑上安装了 Git,并打开终端或命令行界面。 导航到你的仓库目录:使用 cd 命令来切换到包含你想要移动文件夹的仓库的目录。 cd /path/to/your/repository使用 git mv 命令移动文件夹&#x…

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 的。 是否解决说明&…

嵌入式系统 tensorflow

🎬 秋野酱:《个人主页》 🔥 个人专栏:《Java专栏》《Python专栏》 ⛺️心若有所向往,何惧道阻且长 文章目录 探索嵌入式系统中的 TensorFlow:机遇与挑战一、TensorFlow 适配嵌入式的优势二、面临的硬件瓶颈三、软件优化策略四、实…