Python数据分析案例61——信贷风控评分卡模型(A卡)(scorecardpy 全面解析)

案例背景

虽然在效果上,传统的逻辑回归模型通常不如现代的机器学习模型,但在风控领域,解释性至关重要。逻辑回归的解释性是这些“黑箱”模型所无法比拟的,因此,研究传统的评分卡模型依然是有意义的。 传统的评分卡模型与机器学习模型的主要差异在于特征工程的处理。机器学习模型通常不需要对数据进行大幅度的调整,而传统评分卡模型则需要对数据进行分箱、编码、计算WOE(Weight of Evidence)等处理。

并且由于评分卡模型是线性模型,还需要进行一定的变量筛选。因此,评分卡模型的特征工程相对更加复杂,但解释性更强。 至于哪种模型效果更好,取决于具体的数据鄂使用场景(老板非要看解释性那就得评分卡了,客户非要效果那就得LGBM)。在这个案例中,我们使用一个之前测试过的信贷评分数据集来构建评分卡模型,并与主流机器学习模型进行对比,以评估其效果。这个案例也展示了构建全面的A卡(授信行为)和信贷风控评分模型的整体流程,同时解析了ScoreCardPy包的用法,方便未来快速应用于相关项目。

scorecardpy 的这些用法都会演示,全面解析 数据集划分 (split_df)

变量筛选(iv, var_filter)

变量分箱(woebin, woebin_plot, woebin_adj, woebin_ply)

分数转换(scorecard, scorecard_ply)

效果评估(perf_eva, perf_psi)


数据介绍

大部分的教学,一提到ScoreCardPy包应用,马上就要掏出了他内置的德国信用卡数据集。这个其实对新手小白不是很友好的,他们也不知道这个数据集,咋样读取的,怎么做清洗。我下面会从数据读取开始一步步的进行.

本次数据是这样的:

 

最后一列一变量就代表这个人是否会违约,也就是是不是要给他贷款的和响应变量y。所以这是一个a卡模型。

当然需要本次演示的所有代码文件和数据的同学还是可以参考:信贷评分卡


代码实现

首先导入数据科学常用的包

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt 
import seaborn as sns
import scorecardpy as sc
import shapplt.rcParams ['font.sans-serif'] ='SimHei'               #显示中文
plt.rcParams ['axes.unicode_minus']=False               #显示负号

然后读取数据

读取数据

train=pd.read_csv('train.csv').set_index('Id')
test=pd.read_csv('test.csv').set_index('Id')
print(train.shape,test.shape)
train.head()

可以看到训练集7500条,测试集2500条。变量维度为十六个x,一个y。

查看数据基本信息

train.info()

我们可以看到数据的缺失率参差不齐,并且有一些object的对象这些不能放入模型直接训练,得进行一个转化。

变量的含义的简介:

查看一下标签 分布

train['Credit Default'].value_counts()

可以看到大概的比例是好样本为五,坏样本为二,比例还算较为均衡,没有那么极端。

非数值型变量查看其描述性统计。

train.select_dtypes(exclude=['number']).describe()

可以看到这几个类别变量的类别都不是很多,我们可以直接进行因子化,label encoder。

我们自定义一个函数,不仅训练集要进行同样的转化,测试集也要同样进行类别变量的转化。

def ensure_category_consistency(train, test, columns):for col in columns:# 找到所有可能的类别categories = pd.Categorical(train[col]).categories  #.append(test[col])# 定义一致的类别顺序train[col] = pd.Categorical(train[col], categories=categories)test[col] = pd.Categorical(test[col], categories=categories)# 转换为category数据类型train[col] = train[col].astype('category')test[col] = test[col].astype('category')return train, test### 类别变量转化
columns_to_convert = train.select_dtypes(exclude=['number']).columns.to_list()
train, test = ensure_category_consistency(train, test, columns_to_convert)

查看数据信息

train.info()

现在可以看到所有的数据基本上都是数值型和类别型变量,可以直接进行模型计算。


变量筛选

做机器学习其实大体上是不用进行变量筛选的,变量越多越好,但是做传统的评分卡模型,由于它是线性模型,所以它不能用那么多变量,它得进行一定的筛选,得把一些解释性不好的变量,效果不好的变量给它进行去掉。

其主要用法解析:

默认的参数配置为: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”。

下面进行变量筛选,我们使用iv最小为0.02的这个条件。(不懂iv是啥的可以去搜一下)

dt_s = sc.var_filter(train, y="Credit Default",iv_limit=0.02, missing_limit=0.95, identical_limit=0.95,return_rm_reason=True)

我们这里打开了返回原因的这个参数,因为所以它后面会返回一个字典,我们来查看一下这个字典是什么。

dt_s['rm']  ## return_rm_reason=True 返回一个字典

可以看到它移除了5个变量,并且它这个方法返回的rm的这个键对应的值是一个data frame,他清楚地告诉你哪些变量是v小于0.02才导致他们被移除的。

data_train=dt_s['dt']
data_train.info()

现在只剩下十二个变量了,x只有十一个了

我们对测试集也进行同样的变量过滤

### 测试集也进行同样的变量过滤,但是没y
test=test[data_train.columns[:-1]]

这里附赠一个,我自己写的如何能全面的看iv的一个方法吧。

### 变量筛选咋定义看IV的方法
def calculate_pred_proba_bin(true_labels,predictions, bins=10):# 创建分箱区间bin_edges = np.linspace(0, 1, bins + 1)# 分箱bin_labels = [f"{bin_edges[i]:.2f}-{bin_edges[i+1]:.2f}" for i in range(len(bin_edges)-1)]bin_indices = np.digitize(predictions, bin_edges, right=False) - 1# 创建数据框df = pd.DataFrame({ 'bin': [bin_labels[i] for i in bin_indices], 'label': true_labels })# 统计各个分箱的总数、类别为0和1的样本数result = df.groupby('bin')['label'].agg(total='count',count_0=lambda x: (x == 0).sum(),count_1=lambda x: (x == 1).sum()).reset_index()# 计算坏样本率和坏样本在所有坏样本中的比例total_bad_samples = result['count_1'].sum()result['bad_rate'] = result['count_1'] / result['total']result['bad_percent'] = result['count_1'] / total_bad_samples#result=result.sort_values('bin',ascending=False)result['lift']=result['bad_rate']/(total_bad_samples/result['total'].sum())result['cumulative_bad_percent'] = result['bad_percent'][::-1].cumsum()[::-1]return result.style.bar(color='skyblue').format(subset=['bad_rate','bad_percent','lift','cumulative_bad_percent'], precision=4)def scorecardpy_display_bin(bins_info):df_list = []for col, bin_data in bins_info.items():df = pd.DataFrame(bin_data)df_list.append(df)result_df = pd.concat(df_list, ignore_index=True)# 增加 lift 列total_bad = result_df['bad'].sum()   ;   total_count = result_df['count'].sum()overall_bad_rate = total_bad / total_countresult_df['lift'] = result_df['badprob'] / overall_bad_rateresult_df=result_df.sort_values(['total_iv','variable'],ascending=False).set_index(['variable','total_iv','bin'])[['count_distr','count','good','bad','badprob','lift','bin_iv','woe']]return  result_df.style.format(subset=['count','good','bad'], precision=0).format(subset=['count_distr', 'bad','lift','badprob','woe','bin_iv'], precision=4).bar(subset=['badprob','bin_iv','lift'], color=['#d65f5f', '#5fba7d'])
train_miss=train.copy()
train_miss['Years in current job']=train_miss['Years in current job'].astype('str').fillna('missing').astype('category')
bins_adj = sc.woebin(train_miss, y="Credit Default")
scorecardpy_display_bin(bins_adj)

这个是经过排序的,可以清楚看到哪些变量,他们对应的IV是多少,他们的分箱情况是怎么样的,并且每一箱的换样本浓度还有提升度以及他们对iv的贡献都在上面,可能一目了然。


数据集划分

虽然sklearn库有它自带的划分训练集的方式,但是我们的sc包它也有。划分训练集和验证集的方法,我们就用他的好了。

train, val = sc.split_df(data_train, 'Credit Default', ratio=0.7, seed=186).values()
print(train.shape, val.shape )

可以看到我们的训练集大概有5250条数据,12个变量,其中11个是x,一个是y,我们的验证集是2250条。也是12个变量。


变量分箱

可通过woebin()函数对全部变量进行自动分箱,并基于woe_bin的结果,使用woebin_plot对各变量分箱的count distribution和bad probability进行可视化,可观察是否存在单调性:

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;
method: 设置分箱方法,可设置"tree"(决策树)或"chimerge"(卡方),默认值为"tree";

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,不会输出任何信息;

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)

其实使用很简单,就下面一行代码,然后再画图看一看就行。

bins = sc.woebin(data_train, y="Credit Default" )
sc.woebin_plot(bins)

所有变量都有一张图,但是我这里篇幅限制就不放那么多了,只放这一个变量。

从这个图中我们可以清楚的看到它的分箱的情况,每一箱里面的好坏的数量,比例以及坏样本的浓度是怎么样变化的。

## 可以看到 Credit Score 这个字段有点问题,按理来说分数越高黑样本浓度越低 ,750分以上黑样本这么高明显有问题

我们画拉出明细来看一下

bins['Credit Score']#.plot.hist()

这个明细其实就是上面的图,但是他并没有展示750以上的这个数据的分布长什么样,我们直接筛选来看一下。

##查看到750以上的,部分是比750大一点,很多是5000以上的分数,明显有问题,这个切割点需要重新划分,
data_train[data_train["Credit Score"]>800]["Credit Score"].plot.hist()

800以上都是直接跳到了6000这种最高分了,下面重新划分这个变量的分箱阈值

breaks_adj = {'Credit Score': [678,696,728,740,800] }
bins_adj = sc.woebin(data_train, y="Credit Default", breaks_list=breaks_adj)
sc.woebin_plot(bins_adj)

这里就使用到了break list,就是可以自己手工定义分享的节点阈值的。参数我觉得还是挺好用的,可以调整这个分箱,然后重新再看我们的这个变量的情况。

由于分箱多了一箱,所以可能有点密集重叠,但是我们可以明显的看到。信用分数这一项,随着分数变高,它的换样本浓度是明显的下降的只有在800分以上。6000分以上的这种才全是黑样本,这应该是数据的异常情况。

可以看到 iv已经升到了1.6119级,信用卡评分在800分以上的基本全部是黑样本。这应该是数据的一些错误之类的,因为信用卡评分也不可能到五六千分以上。

重新划分后,800分以上的,黑样本100浓度,IV超高。分箱完成后,使用woebin_ply()函数对变量进行woe变换,之后需要把所得到的woe值作为模型的输入。


WOE转化

分箱分好之后,我们就要进行woe的转化编码,在这个包里面使用很简单。

train_woe = sc.woebin_ply(train, bins_adj)
val_woe = sc.woebin_ply(val, bins_adj)
test_woe = sc.woebin_ply(test, bins_adj)

数据现在就准备好了我们可以进行训练了。


模型训练

我们取出x跟y训练集和验证集,还有测试集。

y_train = train_woe.loc[:,'Credit Default']
X_train = train_woe.loc[:,train_woe.columns != 'Credit Default']
y_val = val_woe.loc[:,'Credit Default']
X_val = val_woe.loc[:,train_woe.columns != 'Credit Default']X_test = test_woe.loc[:,train_woe.columns[1:] != 'Credit Default']
print(X_train.shape,y_train.shape,X_val.shape,y_val.shape, X_test.shape)

使用逻辑回归进行学习器

# 逻辑回归 ------
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]
val_pred = lr.predict_proba(X_val)[:,1]test_pred = lr.predict_proba(X_test)[:,1]


效果评估

可使用perf_eva()函数对模型效果进行计算及可视化,基于预测的概率值和label值,提供KS(kolmogorov-smirnow), ROC, lift以及precision-recall四种评估指标:

该函数还包括以下参数:

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"]。
 

他的评估看ks跟auc特别简单,一行代码就可以了。

train_perf = sc.perf_eva(y_train, train_pred, title = "train")
val_perf = sc.perf_eva(y_val, val_pred, title = "val")

训练集KS=0.38,AUC=0.77,验证集KS=0.3716,AUC=0.765,没有过拟合,很好的效果。

到这里可能就有小聪明要问了,为什么不用分类问题常用的评价指标,准确率,精准度,召回率f1值呢。

呃,因为在信贷模型里面大部分的时候都是样本不平衡的,这4个指标在样本不平衡的情况下效果非常差,信贷模型主要还是看ks跟auc。


分数转换

然后基于’woebin'的结果和sklearn.linear_model的LogisticRegression,创建scorecard()函数,用于构建评分卡,只需一行代码:

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。

使用很简单

card = sc.scorecard(bins_adj, lr, X_train.columns)

然后基于scorecard的结果,用scorecard_ply()函数计算train和test数据集的信用分数:

train_score = sc.scorecard_ply(train, card, print_step=0)
val_score = sc.scorecard_ply(val, card, print_step=0)
test_score = sc.scorecard_ply(test, card, print_step=0)

最后用perf_psi()得到该评分卡在测试数据集上的表现。

但是这个方法好像有时候版本问题容易报错,所以我们就自己自定义了一个怎么算psi的方法。

train.shape,val.shape,train_score.shape,val_score.shape , test_score.shapedef calculate_psi(expected, actual, bins=10, epsilon=1e-10):# 分箱breakpoints = np.linspace(0, 1, bins + 1) * (max(expected.max(), actual.max()) - min(expected.min(), actual.min())) + min(expected.min(), actual.min())expected_percents = np.histogram(expected, bins=breakpoints)[0] / len(expected)actual_percents = np.histogram(actual, bins=breakpoints)[0] / len(actual)# 增加一个小的正数,避免零值expected_percents = np.where(expected_percents == 0, epsilon, expected_percents)actual_percents = np.where(actual_percents == 0, epsilon, actual_percents)# 计算PSIpsi_value = np.sum((expected_percents - actual_percents) * np.log(expected_percents / actual_percents))return psi_value
print(f'训练集和验证集的PSI{calculate_psi(train_score.to_numpy().reshape(-1,), val_score.to_numpy().reshape(-1,))}') 
print(f'训练集测试集的PSI{calculate_psi(train_score.to_numpy().reshape(-1,), test_score.to_numpy().reshape(-1,)) }')

可以看到训练集和验证集,还有训练集和测试集的预测的分数PSI变化都不大,因此预测出来的分布都较为稳定,模型性能应该都挺良好的。

plt.figure(figsize=(7, 3),dpi=128)
sns.kdeplot(train_score, bw_adjust=1.5,label='Train Score', fill=True, palette="Set1", color='gold', alpha=0.3)#
sns.kdeplot(val_score,bw_adjust=1.5,label='Validation Score',fill=True,  palette="Set2", alpha=0.3)#
sns.kdeplot(test_score, label='Test Score', fill=True,palette="Set3", alpha=0.3) #plt.title('Kernel Density Plot of Scores')
plt.xlabel('Score')
plt.ylabel('Density')
plt.legend()
plt.show()

高度重叠,效果很好,测试集也没偏移

test_score是信用卡评分,是概率转化后的分数,test_pred是概率,现在重新预测为变成枚举值后 可以储存,然后提交

test_pred = lr.predict(X_test)#[:,1]
df_pred=pd.DataFrame(test_pred,index=[test.index],columns=['Credit Default'])
df_pred.head()

df_pred.to_csv('评分卡模型预测结果.csv')

进行储存,我们测试集上的这些申请人,就知道要不要给他贷款了。


和机器学习模型对比

上面的评分卡模型和机器学习模型到底谁效果好呢?我们测试一下

(之前有全面用这个数据集做过一篇关于机器学习及其可解释性可视化分析的一个案例链接在这儿)

这里的机器学习就是简单的算一个评价指标ks,做一下对比。

重新读取数据

train=pd.read_csv('train.csv').set_index('Id')
test=pd.read_csv('test.csv').set_index('Id')
columns_to_convert = train.select_dtypes(exclude=['number']).columns.to_list()
train, test = ensure_category_consistency(train, test, columns_to_convert)

划分训练集和验证集

X=train.iloc[:,:-1]   ;  y=train.iloc[:,-1]
from sklearn.model_selection import train_test_split
X_train,X_val,y_train,y_val=train_test_split(X,y,stratify=y,test_size=0.3,random_state=186)

定义分类问题常用的四个评价指标

from sklearn.metrics import confusion_matrix
from sklearn.metrics import classification_report
from sklearn.metrics import cohen_kappa_scoredef evaluation(y_test, y_predict):accuracy=classification_report(y_test, y_predict,output_dict=True)['accuracy']s=classification_report(y_test, y_predict,output_dict=True)['weighted avg']precision=s['precision']recall=s['recall']f1_score=s['f1-score']#kappa=cohen_kappa_score(y_test, y_predict)return accuracy,precision,recall,f1_score #, kappa

构建Lgbml模型

from lightgbm import LGBMClassifier
model=LGBMClassifier(objective='binary',random_state=1,verbose=-1,max_depth=6,n_estimators=100,eta=0.05)
model.fit(X_train, y_train)
y_pred=model.predict(X_val)
evaluation(y_val,y_pred)

可以看到这4个指标的数据的情况,但是信贷模型一般不关注他们,所以就不是很重要了。

画出pr曲线和roc图。

from sklearn.metrics import roc_curve, auc, precision_recall_curve
y_pred_proba = model.predict_proba(X_val)[:, 1]
# 计算ROC曲线和AUC值
fpr, tpr, _ = roc_curve(y_val, y_pred_proba)
roc_auc = auc(fpr, tpr)
# 计算PR曲线
precision, recall, _ = precision_recall_curve(y_val, y_pred_proba)# 创建1*2的子图
plt.figure(figsize=(10, 4),dpi=128)# 绘制ROC曲线
plt.subplot(1, 2, 1)
plt.plot(fpr, tpr, color='tomato', lw=2, label='AUC = %0.2f' % roc_auc)
plt.plot([0, 1], [0, 1], color='k', lw=1, linestyle='--')
plt.xlim([0.0, 1.0]) ;  plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')  ;  plt.ylabel('True Positive Rate')
plt.title('Receiver Operating Characteristic (ROC) Curve')
plt.legend(loc="lower right")# 绘制PR曲线
plt.subplot(1, 2, 2)
plt.plot(recall, precision, color='skyblue', lw=2)
plt.xlim([0.0, 1.0])  ;  plt.ylim([0.0, 1.05])
plt.xlabel('Recall')  ;  plt.ylabel('Precision')
plt.title('Precision-Recall (PR) Curve')# 显示图像
plt.tight_layout()
plt.show()

画出ks的图

import scikitplot as skplt
skplt.metrics.plot_ks_statistic(y_val,model.predict_proba(X_val))
plt.show()

Lgbm模型验证集上的KS=0.369,AUC=0.77 ,比上面的评分卡模型验证集KS=0.3716,AUC=0.765差不多

Lgbm模型处理简单,但是没太多解释性,评分卡逻辑回归要做很多woe处理,解释性强,效果都差不多,各有优缺点吧

预测结果储存

test_pred=model.predict(test)
df_pred=pd.DataFrame(test_pred,index=[test.index],columns=['Credit Default'])
df_pred.head()
df_pred.to_csv('LGBM模型预测结果.csv')

那么到底谁的结果好呢?我们把测试集上用两种不同模型得到的结果分别提交到kaggle上去进行一个打分。

 

测试集上传到kaggle上计算F1值的对比,高下立见!

看来还是现代的机器学习效果更好啊!


下面是PS,补充学习内容

评分卡转化的影响

我们模型输出的是概率要转化为评分卡的这个分数,要经过一定的几率转化,这个转化有一些参数,我们可以研究一下它对分数的分布的影响。

import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns# 生成1000个正态分布的随机概率,假设均值在0.5,标准差较小以确保大部分集中在0到1之间
np.random.seed(42)  # 固定随机种子以保证结果可重复
random_probs = np.clip(np.random.normal(loc=0.5, scale=0.1, size=1000), 0, 1)import numpy as npdef cal_score(pred, pdo=0.2, base_score=0.5, base_odds=1.0):factor = pdo / np.log(2)  # 调整这部分offset = base_score - factor * np.log(base_odds)odds = (1 - pred) / predscore = offset + factor * np.log(odds)# 限制分数在0到1之间#score = np.clip(score, score_min, score_max)return score# 应用分数转换
scores = cal_score(random_probs)

调整 pdo 参数:pdo(每翻一倍的分数差)决定了分数随赔率变化的敏感度。通过降低 pdo 值,你可以增加分数对概率变化的敏感度,从而增加区分度。

调整 base_odds 参数:base_odds 是基准的赔率。调整这个值可以影响分数的整体偏移。例如,如果你将基准赔率稍微增大或减小,可能会得到偏离中心的分数。

调整 base_score 参数:base_score 是赔率为基准赔率时的分数。通过改变这个值,你可以整体上平移分数分布。

scores2 = cal_score(random_probs,pdo=0.2, base_score=0.5, base_odds=1,)
scores3 = cal_score(random_probs,pdo=0.5, base_score=0.5, base_odds=1,)
scores4 = cal_score(random_probs,pdo=0.2, base_score=0.05, base_odds=1,)
scores5 = cal_score(random_probs,pdo=0.2, base_score=0.5, base_odds=2,)

可视化对比

# 画核密度图
plt.figure(figsize=(7, 4),dpi=128)
sns.kdeplot(random_probs, fill=True, bw_adjust=1.5,label='real')
sns.kdeplot(scores2, fill=True, bw_adjust=1.5,label='pdo=0.2, base_score=0.5, base_odds=1')
sns.kdeplot(scores3, fill=True, bw_adjust=1.5,label='pdo=0.5, base_score=0.5, base_odds=1')
sns.kdeplot(scores4, fill=True, bw_adjust=1.5,label='pdo=0.2, base_score=0.05, base_odds=1')
sns.kdeplot(scores5, fill=True, bw_adjust=1.5,label='pdo=0.2, base_score=0.5, base_odds=2')
plt.title('KDE of Transformed Probabilities')
plt.xlabel('Probability')
plt.ylabel('Density')
plt.legend(fontsize=6)
plt.show()


创作不易,看官觉得写得还不错的话点个关注和赞吧,本人会持续更新python数据分析领域的代码文章~(需要定制类似的代码可私信)

以往的文章可以在这里查看:数据分析案例合集


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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Weblogic漏洞复现(Vulhub)

0x00前言 1.docker 安装 Docker的安装_docker安装-CSDN博客 2.docker的镜像 1.可以在阿里云上的容器服务找到镜像源。 2.也可以使用下面的镜像源,时快时慢不稳定。 {"registry-mirrors":["https://docker.registry.cyou","https://d…

【Python环境配置-Step1】PyCharm 2024最新官网下载、安装教程

背景: 1、 步骤: 1、PyCharm 官网下载地址:https://www.jetbrains.com/pycharm/ 2、查看下图红框选中,下载社区版(免费) 3、(查看下图红框选中)这里选其他版本 4、这里我下载的…

用vite创建项目

一. vite vue2 1. 全局安装 create-vite npm install -g create-vite 2. 创建项目 进入你想要创建项目的文件夹下 打开 CMD 用 JavaScript create-vite my-vue2-project --template vue 若用 TypeScript 则 create-vite my-vue2-project --template vue-ts 这里的 …

FBX福币交易所A股三大指数小幅低开 稀土永磁板块回调

查查配分析11月5日电 周二,A股三大指数小幅低开。沪指开盘跌0.10%报3306.81点,深证成指开盘跌0.09%报10653.20点,创业板指开盘跌0.05%报2184.90点。 FBX福币凭借用户友好的界面和对透明度的承诺,迅速在加密货币市场中崭露头角,成为广大用户信赖的平台。 来源:同花顺iFinD 盘面…

LeetCode总结-链表

一、遍历链表 1290.二进制链表转整数 2058.找出临界点之间的最小和最大距离 2181.合并零之间的节点 二、删除节点 问:为什么没有修改 dummy,但 dummy.next 却是新链表的头节点?如果删除了 head,那么最后返回的是不是原链表的头…

腐蚀图像分割系统:前端交互展示

腐蚀图像分割系统源码&数据集分享 [yolov8-seg-C2f-DySnakeConv&yolov8-seg-LSKNet等50全套改进创新点发刊_一键训练教程_Web前端展示] 1.研究背景与意义 项目参考ILSVRC ImageNet Large Scale Visual Recognition Challenge 项目来源AAAI Global Al…

NIM 平台生成式 AI-demo

需要python环境 官网注册:(后续调用模型需要秘钥key)Try NVIDIA NIM APIs 可以看到有多种模型: 官方案例 1.安装相关依赖: pip install langchain_nvidia_ai_endpoints langchain-community langchain-text-splitt…

欢迎使用Markdown编辑器

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…

智慧医疗:AI如何改变传统医疗服务模式?

内容概要 在如今的医疗界,智慧医疗正如一阵旋风,呼啸而来,打破了传统的模式。这一变革的核心,毫无疑问是人工智能。想象一下,一个不需要排队候诊、甚至不需要出门的医生——这就是智能助手的非凡魅力!通过…

1.kubernetes作用及组件

容器管理集群名称叫做k8s 容器的编排工具:swarm,kubesphere,open shift,kubernetes【市场占比大】 一.kubernetes介绍 1.kubernetes是什么? 由谷歌公司开源的应用,基于go语言编写 简称k8s 2.kubernet…

【AcWing】算法基础课-动态规划

目录 1、闫式DP分析法 2、背包问题 2.1 01背包问题 朴素版本 优化版本 2.2 完全背包问题 朴素版本 优化版本 2.3 多重背包问题 朴素版本 二进制优化 2.4 分组背包问题 3、线性DP 3.1 数字三角形 3.2 最长上升子序列 3.3 最长公共子序列 4、区间DP 5、数位统计…

白天用的投影仪哪款好?掌握这个亮度参数谁敢忽悠你

人们追求松弛人生的同时,也着眼于高品质的家庭娱乐体验,高端家用投影仪以其大屏幕的视觉冲击力和便捷的移动性,应运成为众多家庭客厅、卧室的新宠。而在挑选家用投影仪时,亮度作为衡量其性能的重要指标之一,直接影响着…

七牛云OSS的使用

图片上传 一、七牛云oss介绍 1.1 图片存储介绍 在实际开发中,我们会有很多处理不同功能的服务器。例如: 应用服务器:负责部署我们的应用 数据库服务器:运行我们的数据库 文件服务器:负责存储用户上传文件的服务器…

重新构想定性数据分析:使用 NVivo 15 实现 AI、反思和备忘录

NVivo 是研究出版物中引用最多的定性数据分析软件 (QDA 软件),使用 NVivo v15 最新主要版本从定性和混合方法数据中发现更多信息,融合 Lumivero AI Assistant 更快地识别主题、运行高级查询和发现基于证据的见解,让您在更短的时间内获得严谨的研究结果。…

C++【string的模拟实现】

在前文我们讲解了string类接口使用(C【string类的使用】(上),C【string类的使用】(下)),本片文章就来模拟实现string类。 注:本文实现的是string的部分重点内容,目的是为了更好的了解string&…

zabbix安装配置与使用

zabbix Zabbix的工作原理如下: 监控部分: Zabbix Agent安装在各个需要监控的主机上,它以主配置的时间间隔(默认60s)收集主机各项指标数据,如CPU占用率、内存使用情况等。 通讯部分: Agent会把收集的数据通过安全通道(默认10051端口)发送到Zabbix Server。Server会存储这些数…

7.3、实验三:RIPv2的基本配置

源文件: 7.3、实验三:RIPv2的基本配置: https://url02.ctfile.com/d/61945102-63684790-45f44b?p2707 (访问密码: 2707) 一、目的 能够使用RIPv2路由协议 二、实验要求 1.要求 使用RIPv2协议,使得PC0 和 Service0能够通信,…

石岩田心村的地面停车点(月卡350)

​我之前一直以为城中村里的地面停车场会比上屋地铁口的联天停车场便宜一些。没想到这个田心村月卡也是350元哈。比对面的园岭村还贵,元岭村月卡我记得才260元。 田心村停车场标识牌 序号 收费项目 收费标准 1 小车临时停放 10元/小时,超过1小时加收…

大模型学习笔记------CLIP模型的再思考

大模型学习笔记------CLIP模型的再思考 1、CLIP模型与Prompt(提示)的思考2、CLIP模型与ResNet等分类模型的根本区别3、结束语 上文已经讲 CLIP(Contrastive Language-Image Pretraining)这个模型,也讲了我的一些思考。但是,随着深…

Spring之依赖注入(DI)和控制反转(IoC)——配置文件、纯注解

依赖注入 依赖注入(Dependency Injection,简称 DI)与控制反转(loC)的含义相同,只不过这两 个称呼是从两个角度描述的同一个概念。对于一个 Spring 初学者来说,这两种称呼很难理解, 下面我们将通过简单的语言来描述这两个概念。 当Java对象&…