Python數據分析案例79——基于征信數據開發信貸風控模型

背景

雖然模型基本都是表格數據那一套了,算法都沒什么新鮮點,但是本次數據還是很值得寫個案例的,有征信數據,各種,個人,機構,逾期匯總.....

這么多特征來做機器學習模型應該還不錯。本次帶來,在實際生產業務中,拿到一大堆的表,怎么對這些數據進行合并,清洗整理,特征工程,然后機器學習建模,評估模型效果。


數據介紹

本次數據主要分這么多表格:

"contest_basic
(基礎表-數據集)"
"contest_ext_crd_hd_report
(機構版征信-報告主表)"

"contest_ext_crd_cd_ln
(機構版征信-貸款)"

"contest_ext_crd_cd_lnd
(機構版征信-貸記卡)"

"contest_ext_crd_is_creditcue
(機構版征信-信用提示)"

"contest_ext_crd_is_sharedebt
(機構版征信-未銷戶貸記卡或者未結清貸款)"

"contest_ext_crd_is_ovdsummary
(機構版征信-逾期(透支)信息匯總)"

"contest_ext_crd_qr_recordsmr
(機構版征信-查詢記錄匯總)"

"contest_ext_crd_qr_recorddtlinfo
(機構版征信-信貸審批查詢記錄明細)"

"contest_ext_crd_cd_ln_spl?
(機構版征信-貸款特殊交易)"

"contest_ext_crd_cd_lnd_ovd
(機構版征信-貸記卡逾期/透支記錄)"

表格里面都有各種的詳細的字段

太多了就不詳細列舉了。數據都放在了“原始數據”這個文件夾里面方便下面代碼讀取。

當然需要本文的全部案例數據和代碼文件的同學還是可以參考:征信風控

實驗過程與結果

  • 數據預處理(空值處理、異常值處理、變量標注、獨熱編碼......)
  • 數據可視化
  • 特征工程
  • 實驗過程
  • 實驗結果

代碼實現

下面開始寫代碼吧

導入包

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import os,randomplt.rcParams["font.sans-serif"] = ["SimHei"]  # 設置顯示中文字體
plt.rcParams["axes.unicode_minus"] = False  # 設置正常顯示符號

數據的合并整理?

讀取合并數據文件,由于這個數據里面的征信不同,來源的數據集太多,所以文件會很亂,做機器學習首先得把他們這些數據都合并一起,就得做很多清洗和處理,下面就開始這樣合并和整理的過程。

#查看數據文件夾里面的所有數據文件的名稱
data_lis=os.listdir('原始數據')
data_lis

#循環讀取所有數據,放入列表?


def read_and_merge_files(folder_path):# 用于存儲所有數據框的列表dataframes = []# 遍歷文件夾中的所有文件for filename in os.listdir(folder_path):file_path = os.path.join(folder_path, filename)# 根據文件后綴使用不同的方法讀取if filename.endswith('.csv'):df = pd.read_csv(file_path)elif filename.endswith('.tsv'):df = pd.read_csv(file_path, sep='\t')else:continue  # 忽略非CSV/TSV文件df.columns=[c.lower() for c in df.columns]print(f"{filename}讀取完成,數據形狀為{df.shape}")dataframes.append(df)return dataframes

讀取所有的文件?

folder_path = '原始數據'  # 替換為文件夾路徑
dataframes_list = read_and_merge_files(folder_path)
len(dataframes_list)

所有的數據合并需要按照report_id進行關聯,但是有的表可能report_id可能會重復,所以先查看一下

#查看數據信息,report_id有重復
def check_files(dataframes):# 確保至少有一個數據框if not dataframes:raise ValueError("No CSV or TSV files found in the directory.")# 依次檢查所有表的report_id數量for i,df in enumerate(dataframes):duplicates = df['report_id'].duplicated().sum()print(f'表{data_lis[i]}中重復的 report_id 數量: {duplicates}')

### 查看每個表的重復情況
?

check_files(dataframes_list)?

對這些重復數據選取最后一條進行合并,表示最新最近的情況

### 合并的時候不需要的字段就過濾掉

unuseless=["query_date","querier",'get_time',    #查詢,信息更新的日期"scheduled_payment_date", "scheduled_payment_amount", "actual_payment_amount", "recent_pay_date",  #本月、最近的應還什么的,沒有時效性,去掉"finance_org", "currency",  "open_date",'end_date',    #日期類都去掉'first_loan_open_month','first_loancard_open_month','first_sl_open_month', 'loan_date','month_dw','report_create_time','payment_state', #一些ID類,編碼類 也都去掉'id_card','loan_id','loancard_id','content' ]def merge_files(dataframes):# 使用第一個表(假設是contest_basic)作為左連接底表main_df = dataframes[0]for i,df in enumerate(dataframes[1:]):#按照report_id 去重保留最后一個df=df[[col for col in df.columns if col not in unuseless]]df=df.drop_duplicates(subset='report_id', keep='last')print(f'正在合并數據表:{data_lis[1:][i]}...')# 按照'report_id'列進行左連接name=data_lis[1:][i].split('_')[-1]main_df = pd.merge(main_df, df, on='report_id', how='left',suffixes=('', f'_@{name}')) ## 名稱重復的特征后面加上@和數據文件名稱print(f'合并之后的數據形狀{main_df.shape}')return main_df

使用這個函數

threshold = len(df_all) * 0.5df_all = df_all.dropna(axis=1, thresh=threshold)
df_all.shape
df_all=merge_files(dataframes_list)

?# 刪除缺失率高達50%的列

threshold = len(df_all) * 0.5df_all = df_all.dropna(axis=1, thresh=threshold)
df_all.shape

### 去除一些 id列(唯一編碼,對模型沒有用),設置report_id為索引
for c in ['id_card','loan_date','loan_id','loancard_id']:if c in df_all.columns:df_all.drop(c,axis=1,inplace=True)
df_all=df_all.set_index('report_id')

## 查看數據信息

df_all.info()

文本的變量很多,需要處理,我們首先查看文本變量的信息

### 查看文本變量的信息
df_all.select_dtypes(include='object').describe().T

##查看字符變量名稱

str_columns=df_all.select_dtypes(include='object').columns.to_list()
str_columns

df_all.select_dtypes(exclude='object').describe().T

數據洗完了,儲存一下吧

### 查看了上述信息,數據合并沒問題,進行儲存
df_all.to_csv('合并后的數據.csv')


數據預處理

數據預處理(空值處理、異常值處理、變量標注、獨熱編碼......)

缺失值查看

### 讀取合并的數據


df=pd.read_csv('合并后的數據.csv').set_index('report_id')
df.head(3)

### 查看標簽y的比例?


df['y'].value_counts()

查看數據信息

df.info()

這樣看可能不全面不直觀,我們觀察缺失值情況直接畫圖就行了

#觀察缺失值
import missingno as msno
msno.matrix(df)

可以很清晰的看到哪些列的缺失率比較多,并且這些缺失都是某些樣本造成的,可能是合并數據的時候,某些人某些身份證沒有這一類的數據。

變量預處理

#取值唯一的變量刪除

for col in df.columns:if len(df[col].value_counts())==1:print(col)df.drop(col,axis=1,inplace=True)

這兩個變量取值唯一就刪除掉了。

#查看數值型數據,


#pd.set_option('display.max_columns', 30)
df.select_dtypes(exclude=['object']).describe()

做機器學習當然需要特征越分散越好,因為這樣就可以在X上更加有區分度,從而更好的分類。所以那些數據分布很集中的變量可以扔掉。我們用異眾比例來衡量數據的分散程度

#計算異眾比例?


variation_ratio_s=0.05
for col in df.select_dtypes(exclude=['object']).columns:df_count=df[col].value_counts()kind=df_count.index[0]variation_ratio=1-(df_count.iloc[0]/len(df[col]))if variation_ratio<variation_ratio_s:print(f'{col} 最多的取值為{kind},異眾比例為{round(variation_ratio,4)},太小了,沒區分度刪掉')df.drop(col,axis=1,inplace=True)

?

##查看非數值型數據
?

df.select_dtypes(exclude=['int64','float64']).describe()

基本都是可以轉為類別型變量

空值處理

缺失值有很多填充方式,可以用中位數,均值,眾數。 也可以就采用那一行前面一個或者后面一個有效值去填充空的

#數值型變量使用均值填充
#文本型數據使用眾數填充
def fill_missing_values(df):# 選擇數值型變量并使用均值填充缺失值for col in df.select_dtypes(include=['int64', 'float64']).columns:mean_value = df[col].mean()df[col].fillna(mean_value, inplace=True)# 選擇字符型變量并使用眾數填充缺失值for col in df.select_dtypes(exclude=['int64', 'float64']).columns:mode_value = df[col].mode()[0]df[col].fillna(mode_value, inplace=True)return df
df=fill_missing_values(df)

查看數據信息

df.info()

可以看到所有的變量沒有缺失值了。

異常值處理

主要是對數值型變量,查看是否有驗證的異常值處理

df_number=df.select_dtypes(include=['int64', 'float64'])
#X異常值處理,先標準化
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
df_number_s = scaler.fit_transform(df_number)

可視化一下看看異常值

plt.figure(figsize=(20,8))
plt.boxplot(x=df_number_s,labels=df_number.columns)
#plt.hlines([-10,10],0,len(columns))
plt.show()

可以看到,很多變量都存在極大值,我們需要進行異常值處理

這個函數傳入三個參數,要處理的數據框,異常值多的變量列名,還有篩掉幾倍的方差。我下面選用的是10,也是就說如果一個樣本數據大于整體10倍的標準差之外就篩掉。

#異常值多的列進行處理
def deal_outline(data,col,n):   #數據,要處理的列名,幾倍的方差for c in col:mean=data[c].mean()std=data[c].std()data=data[(data[c]>mean-n*std)&(data[c]<mean+n*std)]#print(data.shape)return data
df_number=deal_outline(df_number,df_number.columns,10)
df_number.shape

?

### 處理完成后將數據篩選給原始數據
df=df.loc[df_number.index,:]
df.shape

?這樣,原始3w數據減少了594條

變量標注、獨熱編碼......

由于這里分類變量有點多,并且每個變量的類別的取值也較多,使用獨熱編碼可能造成維度災難,所以我們這里進行labelencode,因子化處理類別變量

df_category=df.copy()
#選出列表變量
categorical_feature=df.select_dtypes(include=['category','object']).columns.to_list()
for col in categorical_feature:df[col]=df[col].astype('category').cat.codes    #因子化
df.shape
df.info()  

可以看到所有的變量都變成數值型變量了,下面進行可視化


數據可視化

類別變量可視化

### 我們對類別變量畫柱狀圖
len(df_category.select_dtypes(exclude=['int64', 'float64']).columns)
# Select non-numeric columns
non_numeric_columns = df_category.select_dtypes(exclude=['int64', 'float64']).columns
f, axes = plt.subplots(4, 4, figsize=(14,14),dpi=256)
# Flatten axes for easy iterating
axes_flat = axes.flatten()
for i, column in enumerate(non_numeric_columns):if i < 15:  sns.countplot(x=column, data=df_category, ax=axes_flat[i])axes_flat[i].set_title(f'Count of {column}')for label in axes_flat[i].get_xticklabels():label.set_rotation(90)   #類別標簽旋轉一下,免得多了堆疊看不清# Hide any unused subplots
for j in range(i + 1, 15):f.delaxes(axes_flat[j])plt.tight_layout()
plt.show()

可以很清楚的看到每一個類別變量的哪些類別比較多的類別分布。

數值變量可視化

#畫密度圖,
num_columns = df.select_dtypes(include=['int64', 'float64']).columns.tolist() # 列表頭
dis_cols = 4                   #一行幾個
dis_rows = len(num_columns)
plt.figure(figsize=(3 * dis_cols, 2 * dis_rows),dpi=256)for i in range(len(num_columns)):ax = plt.subplot(dis_rows, dis_cols, i+1)ax = sns.kdeplot(df[num_columns[i]], color="skyblue" ,fill=True)ax.set_xlabel(num_columns[i],fontsize = 14)
plt.tight_layout()
#plt.savefig('訓練測試特征變量核密度圖',formate='png',dpi=500)
plt.show()

具體的結論就不一個變量一個變量的看了,可以很清楚的看到每一個變量的一些分布的特點。例如他們基本都是右偏分布,也就是說具有一些極大值。

和y進行變量之間的相關性研究

# 將數據按 'edu_level' 和 'y' 進行分組,然后計數
edu_vs_y = df_category.groupby(['edu_level', 'y']).size().unstack()# 將數據按 'marry_status' 和 'y' 進行分組,然后計數
marry_vs_y = df_category.groupby(['marry_status', 'y']).size().unstack()# 創建子圖
fig, axes = plt.subplots(1, 2, figsize=(12, 4), dpi=128)# 繪制第一個子圖的分組條形圖
edu_vs_y.plot(kind='bar', stacked=False, ax=axes[0])
axes[0].set_title('不同教育水平的違約情況')
axes[0].set_xlabel('教育水平')
axes[0].set_ylabel('計數')
axes[0].legend(title='是否違約', loc='upper right')# 繪制第二個子圖的分組條形圖
marry_vs_y.plot(kind='bar', stacked=False, ax=axes[1])
axes[1].set_title('不同婚姻狀態的違約情況')
axes[1].set_xlabel('婚姻狀態')
axes[1].set_ylabel('計數')
axes[1].legend(title='是否違約', loc='upper right')# 調整子圖之間的間距
plt.tight_layout()# 顯示圖形
plt.show()

## 看不出明顯的區別

計算相關性系數的熱力圖

corr = plt.subplots(figsize = (18,16),dpi=128)
corr= sns.heatmap(df.corr(method='spearman'),annot=True,square=True)

變量有點多,所以說這里就不詳細寫哪些變量和哪些變量之間的相關性比較高了。


特征工程

## 前面對數據已經進行了很多預處理了,現在就是要對數據進行進行標準化

#首先取出X和y

X=df.drop('y',axis=1)
y=df['y']

?標準化

# 進行標準化
#數據標準化
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
scaler.fit(X)
X_s = scaler.transform(X)
print('標準化后數據形狀:')
print(X_s.shape,y.shape)


實驗過程

機器學習——模型選擇

我們首先定義分類問題所使用的評價指標:準確率,精準度,召回率,F1值

定義評價指標

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

?

訓練集測試集劃分
#劃分訓練集和測試集
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test=train_test_split(X_s,y,stratify=y,test_size=0.2,random_state=0)
print(X_train.shape,X_test.shape,y_train.shape,y_test.shape)

### 查看訓練和測試集的y的黑白占比
?

y_train.value_counts(normalize=True)

y_test.value_counts(normalize=True)

?兩者比例是類似的,不過樣本很不平衡,取值為1的太少了,所以我們要做一點采樣處理。

在處理極度不平衡的分類樣本時,可以考慮以下幾種方法: 欠采樣(Undersampling):從多數類別中隨機選擇一部分樣本,使得多數類別的樣本數量與少數類別的樣本數量相近。這種方法的優點是簡單快捷,但可能會丟失一些有用信息。

過采樣(Oversampling):從少數類別中隨機復制一些樣本,使得少數類別的樣本數量與多數類別的樣本數量相近。這種方法的優點是可以充分利用數據集,但可能會導致過擬合。

SMOTE(Synthetic Minority Over-sampling Technique)算法:是一種常用的過采樣方法,它通過對少數類別樣本進行插值生成新的樣本來擴充數據集。這種方法可以有效地避免過擬合問題。

混合采樣(Mixed Sampling):結合欠采樣和過采樣的優點,既可以減少數據量,又可以充分利用數據集。可以先進行欠采樣,然后再對欠采樣后的數據進行過采樣。

我們首先對樣本少的類別,即逾期類別,取值為1 的進行上采樣。

from imblearn.over_sampling import RandomOverSampler
os=RandomOverSampler(sampling_strategy=0.1)
X_train_ns,y_train_ns=os.fit_resample(X_s,y)
print("The number of classes before fit {}".format(y.value_counts().to_dict()))
print("The number of classes after fit {}".format(y_train_ns.value_counts().to_dict()))

X_train_ns.shape,y_train_ns.shape

再對樣本多的進行下采樣,比例為0.2,即0類數量8成,1類數量2成。雖然也不平衡,但是比剛剛那個好多了。。也能訓練了。

from imblearn.under_sampling import RandomUnderSampler
rus = RandomUnderSampler(sampling_strategy=0.25)
X_resampled, y_resampled = rus.fit_resample(X_train_ns, y_train_ns)
X_train_ns2,y_train_ns2=rus.fit_resample(X_train_ns,y_train_ns)
print("The number of classes before fit {}".format(y_train_ns.value_counts().to_dict()))
print("The number of classes after fit {}".format(y_train_ns2.value_counts().to_dict()))

查看形狀

print(X_train_ns2.shape,y_train_ns2.shape)

##再次重新劃分訓練集和測試集
X_train,X_test,y_train,y_test=train_test_split(X_train_ns2,y_train_ns2,stratify=y_train_ns2,test_size=0.2,random_state=0)
print(X_train.shape,X_test.shape,y_train.shape,y_test.shape)

### 查看訓練和測試集的y的黑白占比
y_train.value_counts(normalize=True)

y_test.value_counts(normalize=True)

比例大概8:2,比剛剛嚴重不平衡好很多,可以進行機器學習了


模型訓練

#導包
from sklearn.linear_model import LogisticRegression
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import GradientBoostingClassifier
from xgboost.sklearn import XGBClassifier
from lightgbm import LGBMClassifier
from sklearn.svm import SVC
from sklearn.neural_network import MLPClassifier

實例化分類器:

#邏輯回歸
model1 =  LogisticRegression(C=1e10,max_iter=10000)#線性判別分析
model2 = LinearDiscriminantAnalysis()#K近鄰
model3 = KNeighborsClassifier(n_neighbors=10)#決策樹
model4 = DecisionTreeClassifier(random_state=77)#隨機森林
model5= RandomForestClassifier(n_estimators=1000,  max_features='sqrt',random_state=10)#梯度提升
model6 = GradientBoostingClassifier(random_state=123)#極端梯度提升
model7 =  XGBClassifier(objective='binary:logistic',random_state=1)#輕量梯度提升
model8 = LGBMClassifier(objective='binary',random_state=1,verbose=-1)#支持向量機
model9 = SVC(kernel="rbf", random_state=77)#神經網絡
model10 = MLPClassifier(hidden_layer_sizes=(16,8), random_state=77, max_iter=10000)model_list=[model1,model2,model3,model4,model5,model6,model7,model8,model9,model10]
model_name=['邏輯回歸','線性判別','K近鄰','決策樹','隨機森林','梯度提升','極端梯度提升','輕量梯度提升','支持向量機','神經網絡']

訓練和評估

df_eval=pd.DataFrame(columns=['Accuracy','Precision','Recall','F1_score'])
for i in range(10):model_C=model_list[i]name=model_name[i]model_C.fit(X_train, y_train)pred=model_C.predict(X_test)#s=classification_report(y_test, pred)s=evaluation(y_test,pred)df_eval.loc[name,:]=list(s)print(f'{name}模型完成')

查看評價指標

df_eval

可視化看看

import matplotlib.pyplot as plt 
plt.rcParams['font.sans-serif'] = ['KaiTi']  #中文
plt.rcParams['axes.unicode_minus'] = False   #負號bar_width = 0.4
colors=['c', 'b', 'g', 'tomato', 'm', 'y', 'lime', 'k','orange','pink','grey','tan']
fig, ax = plt.subplots(2,2,figsize=(10,8),dpi=128)
for i,col in enumerate(df_eval.columns):n=int(str('22')+str(i+1))plt.subplot(n)df_col=df_eval[col]m =np.arange(len(df_col))plt.bar(x=m,height=df_col.to_numpy(),width=bar_width,color=colors)#plt.xlabel('Methods',fontsize=12)names=df_col.indexplt.xticks(range(len(df_col)),names,fontsize=10)plt.xticks(rotation=40)plt.ylabel(col,fontsize=14)plt.tight_layout()
#plt.savefig('柱狀圖.jpg',dpi=512)
plt.show()

可以看到隨機森林模型效果最好,我們選擇他作為最終模型

超參數搜索

簡單網格化定義幾個超參數,搜索一下。對模型的效果進行一定的優化。

水系森林超凡樹不太多,我們這里就搜索一下最影響最大的估計器個數吧。

# 網格化搜索最優超參數
from sklearn.model_selection import KFold, train_test_split, GridSearchCV
rf_model = RandomForestClassifier(max_features='sqrt',random_state=10)
param_dict = { 'n_estimators': [100,500,700]}
clf = GridSearchCV(rf_model, param_dict, verbose=1,cv=3)
clf.fit(X_train, y_train)
print(clf.best_score_)
print(clf.best_params_)

將搜索出來的這個超參數代入模型去訓練

model=RandomForestClassifier( n_estimators=700   , max_features='sqrt',random_state=10)
model.fit(X_train, y_train)
y_pred=model.predict(X_test)
evaluation(y_test,y_pred)

超參數 可以看到整體的準確率精準度召回率f1值,都稍微提高了一些。

?


實驗結果

AUC,ROC,KS

信貸場景是一個經典的2分類問題,就是判斷它是不是會違約,從而做出要不要給他貸款的決策。2分類問題就逃不開要計算roc曲線,計算auc值和ks。 用如下的代碼畫出roc曲線跟pr曲線。

from sklearn.metrics import roc_curve, auc, precision_recall_curve
y_pred_proba = model.predict_proba(X_test)[:, 1]
# 計算ROC曲線和AUC值
fpr, tpr, _ = roc_curve(y_test, y_pred_proba)
roc_auc = auc(fpr, tpr)
# 計算PR曲線
precision, recall, _ = precision_recall_curve(y_test, 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_test,model.predict_proba(X_test))
plt.show()

模型的AUC0.92, KS0.674,說明模型效果很好,對好壞客戶有很強的區分能力

進一步的去觀察他每個預測的概率區間里面有多少個樣本,多少個好樣本,壞樣本,壞樣本的比例有多少,它的提升度有多少,累積的這個壞樣本的百分比有多少。

自定義函數

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'])
calculate_pred_proba_bin(y_test,y_pred_proba, bins=20)

基本上隨著模型的概率越來越高,模型預測正確的把握越來越大,其提升度也就是每一箱的這個黑樣本的比例也是越來越高的。說明模型其性能沒有什么太多的問題。

如果要進行使用的話建議是可以模型預測概率為0.6區間以上的全部拒絕掉,可以過濾到50%的黑樣本,0.15到0.5之間的樣本再進行一次審查,0.15以下的貸款可以接受.

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/92232.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/92232.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/92232.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

板凳-------Mysql cookbook學習 (十二--------3_2)

3.3鏈接表 結構 P79頁 用一個類圖來表示EmployeeNode類的結構&#xff0c;展示其屬性和關系&#xff1a; plaintext ----------------------------------------- | EmployeeNode | ----------------------------------------- | - emp_no: int …

深度學習圖像預處理:統一輸入圖像尺寸方案

在實際訓練中&#xff0c;最常見也最簡單的做法&#xff0c;就是在送入網絡前把所有圖片「變形」到同一個分辨率&#xff08;比如 256256 或 224224&#xff09;&#xff0c;或者先裁剪&#xff0f;填充成同樣大小。具體而言&#xff0c;可以分成以下幾類方案&#xff1a;一、圖…

pytest-log

問題1&#xff1a;我們在運行測試用例的時候如何記錄測試的log&#xff0c;如何使用&#xff1f;問題2&#xff1a;我寫的函數&#xff0c;為了方便log記錄&#xff0c;但是在pytest運行時&#xff0c;會兼容pytest且不會重復記錄&#xff0c;怎么解決&#xff1f;1、pytest有內…

在安卓源碼中添加自定義jar包給源碼中某些模塊使用

一、具體步驟 1. 準備目錄與 Jar 包 在vendor下 創建新的模塊目錄&#xff0c;放入demo.jar 包&#xff1a; demojar/ # 模塊目錄 ├── Android.bp # 編譯配置文件 └── demo.jar 2. 編寫 Android.bp 配置 Android.bp 示例配置&#xff1a; java_import {…

buntu 22.04 上離線安裝Docker 25.0.5(二)

以下有免費的4090云主機提供ubuntu22.04系統的其他入門實踐操作 地址&#xff1a;星宇科技 | GPU服務器 高性能云主機 云服務器-登錄 相關兌換碼星宇社區---4090算力卡免費體驗、共享開發社區-CSDN博客 兌換碼要是過期了&#xff0c;可以私信我獲取最新兌換碼&#xff01;&a…

初探 Web 環境下的 LLM 安全:攻擊原理與風險邊界

文章目錄前言1 什么是大型語言模型&#xff08;LLM&#xff09;&#xff1f;1.1 LLM的核心特征1.2 LLM在Web場景中的典型應用2 LLM攻擊的核心手段&#xff1a;提示注入與權限濫用3 LLM與API集成的安全隱患&#xff1a;工作流中的漏洞節點3.1 LLM-API集成的典型工作流3.2 工作流…

【新手向】PyTorch常用Tensor shape變換方法

【新手向】PyTorch常用Tensor shape變換方法 前言 B站UP主科研水神大隊長的視頻中介紹了“縫合模塊”大法&#xff0c;其中專門強調了“深度學習 玩的就是shape”。受此啟發&#xff0c;專門整理能夠調整tensor形狀的幾個內置函數&#xff0c;方便以后更好地調整PyTorch代碼中的…

React 18 vs Vue3:狀態管理方案深度對比

?? 背景: React有Redux、Zustand、Jotai等方案 Vue有Pinia、Vuex 4.x 如何選擇適合項目的方案? ?? 核心對比: 維度 React (Redux Toolkit) Vue3 (Pinia) 類型安全 ? 需手動配置TS ? 自動類型推導 代碼量 較多(需寫action) 較少(類似Vuex 5) 響應式原理 不可變數據…

UE5網絡聯機函數

Find Sessions Create Session Join Session Destroy Session Steam是p2p直接聯機 一、steam提供的測試用AppId AppId是steam為每一款游戲所設定的獨有標識&#xff0c;每一款要上架steam的游戲都會擁有獨一無二的AppId。不過為了方便開發者測試&#xff0c;steam提供了游…

Spring Boot 監控:AOP vs Filter vs Java Agent

01前言 在 高并發 微服務 中&#xff0c; 傳統 手動埋點&#xff08;System.currentTimeMillis()&#xff09;就像用體溫計量火箭速度——代碼侵入、重復勞動、維護爆炸。 下文是無侵入、高精度、全鏈路 監控 API 耗時&#xff0c;全程不碰業務代碼的方案&#xff01; 02實戰&…

基于Android的電子記賬本系統

博主介紹&#xff1a;java高級開發&#xff0c;從事互聯網行業多年&#xff0c;熟悉各種主流語言&#xff0c;精通java、python、php、爬蟲、web開發&#xff0c;已經做了多年的畢業設計程序開發&#xff0c;開發過上千套畢業設計程序&#xff0c;沒有什么華麗的語言&#xff0…

7月17日日記

結束了數學建模之后的這兩天一直在緊張的復習&#xff0c;但是說實話效率有點低&#xff0c;因為可能覺得自己找到了兩個小時速成課&#xff0c;覺得無所謂了&#xff0c;所以有點放松了。在宿舍杰哥和林雨城卻一直在復習&#xff0c;感覺他們的微積分和線性代數復習的都比我好…

Linux下SPI設備驅動開發

一.SPI協議介紹1.硬件連接介紹引腳含義&#xff1a;DO(MOSI)&#xff1a;Master Output, Slave Input&#xff0c;SPI主控用來發出數據&#xff0c;SPI從設備用來接收數據。DI(MISO)&#xff1a;Master Input, Slave Output&#xff0c;SPI主控用來發出數據&#xff0c;SPI從設…

用Dify構建氣象智能體:從0到1搭建AI工作流實戰指南

作為一名Agent產品經理,我最近在負責氣象智能體的建設項目。傳統氣象服務面臨三大痛點:數據孤島嚴重(氣象局API、衛星云圖、地面觀測站等多源數據格式不一)、響應鏈路長(從數據采集到預警發布需人工介入多個環節)、交互體驗單一(用戶只能被動接收標準化預警,無法個性化…

Android NDK ffmpeg 音視頻開發實戰

文章目錄接入FFmpeg1.下載FFmpeg 源碼2.編譯FFmpeg.so庫異常處理3.自定義FFmpeg交互so庫創建4.配置CMakeLists.txt5.CMakeLists.txt 環境配置6.Native與Java層調用解碼器準備接入FFmpeg 1.下載FFmpeg 源碼 FFmpeg官網地址 2.編譯FFmpeg.so庫 移動 FFmpeg 源碼文件夾至 Andr…

使用 go-redis-entraid 實現 Entra ID 無密鑰認證

1、依賴與安裝 步驟命令說明安裝&#xff08;或升級&#xff09; go-redis v9.9go get github.com/redis/go-redis/v9latestentraid 必須 ≥ 9.9.0安裝 go-redis-entraidgo get github.com/redis/go-redis-entraid自動拉取 transit 依賴 2、認證方式一覽 方式說明創建 Stream…

window上docker安裝RabbitMQ

1、要進http://localhost:15672管理頁面需要安裝management版本2、搜索鏡像并pull3、啟動鏡像時將端口映射出來4、啟動成功&#xff0c;點擊可查看日志詳情&#xff0c;瀏覽器訪問5、直接使用guest/guest登錄會報錯User can only log in via localhost解決辦法有兩個&#xff1…

異世界歷險之數據結構世界(排序(插入,希爾,堆排))

前言 介紹 插入排序 基本知識&#xff1a; 直接插入排序是一種簡單的插入排序法&#xff0c;其基本思想是&#xff1a; 把待排序的記錄按其關鍵碼值的大小逐個插入到一個已經排好序的有序序列中&#xff0c;直到所有的記錄插入完為止&#xff0c;得到一個新的有序序列 直接插入…

oracle 數據庫中,將幾張表的數據按指定日期范圍實時同步至同一個數據庫的備份表中。

以下是一個Oracle數據庫中實現表數據按指定日期范圍實時同步至備份表的解決方案。這個方案使用存儲過程和觸發器組合實現&#xff1a; 1. 創建備份表結構 首先需要為每張需要備份的表創建對應的備份表&#xff0c;結構與原表相同&#xff1a; -- 為原表創建備份表&#xff08;示…

電腦網絡連接正常,微信、QQ能正常使用,但無法訪問網頁?DNS問題的解決方案和背后原理。

文章目錄1. 問題背景2. 解決方案2.1 手動刷新DNS2.1.1 Windows版本2.1.2 Mac版本2.2 手動設置DNS服務器2.2.1 Windows版2.2.2 Mac版2.3 其他解決方案3. DNS是什么&#xff1f;3.1 詳細解釋DNS3.1.1 A distributed, hierarchical database&#xff08;一個分布式和分層數據庫結構…