Python数据分析案例——基于征信数据开发信贷风控模型
本文基于全面新鲜的征信数据集,构建了一套信贷风控模型。首先完成数据加载、预处理、特征工程(基础合并)和模型训练评估的基础流程,提出特征工程优化方向,为后续挖掘提供思路。采用逻辑回归和随机森林两种模型,通过AUC等核心指标评估性能。最终输出可直接运行的完整代码,包括数据预处理、模型训练、评估及可视化分析等环节。特征工程方面建议进一步衍生统计算子、差值比率、时间切片和行为序列等特征,以提升模型性能。
基于表格数据建模这一套已经做烂了,前面的案例非常多,本次这个案例主要现在于这个数据集是比较全面新鲜的。有很多征信报告的特征,当然本文只是简单地把它们按照某一个组件进行粗暴的合并。呃,如果有探索欲的同学可以更加详细的丰富特征,比如计算最大最小的算子,或者是计算一些差值,时间切片,从而衍生出更丰富的特征工程,进行更加详细的数据挖掘。说不定能够取得更好的效果。
信贷风控是金融领域的核心环节,其核心目标是通过数据模型识别借款人的违约风险,为放贷决策提供依据。传统表格数据建模虽已广泛应用,但本案例的核心优势在于采用全面且新鲜的征信数据集,包含多维度征信报告特征。
本文将先完成基础建模流程,包括数据加载、预处理、特征工程(基础合并)、模型训练与评估,再提出特征工程的优化方向,为有探索欲的使用者提供进一步挖掘的思路,最终输出可直接运行的完整代码与分析过程。
一、案例背景与数据说明
1. 案例背景
信贷机构在放贷前需评估借款人的还款能力与意愿,征信数据是关键依据。它涵盖借款人的信贷历史、负债情况、履约记录等信息,能有效反映其信用状况。
本案例使用的征信数据集,相较于传统数据集,特征维度更丰富,包含征信报告中的多个核心组件(如信贷账户信息、逾期记录、查询记录等),可更全面地刻画借款人风险。
2. 数据字段说明
数据集包含两类核心数据:借款人基础信息表与征信特征表,具体字段如下(示例):
| 表类型 | 字段名称 | 字段说明 | 数据类型 |
|---|---|---|---|
| 借款人基础信息表 | user_id | 借款人唯一标识 | 字符串 |
| 借款人基础信息表 | target | 违约标签(1 = 违约,0 = 正常) | 整数 |
| 征信特征表 | user_id | 借款人唯一标识(关联键) | 字符串 |
| 征信特征表 | credit_account_num | 信贷账户数量 | 整数 |
| 征信特征表 | overdue_1m_count | 1 个月内逾期次数 | 整数 |
| 征信特征表 | max_overdue_days | 最大逾期天数 | 整数 |
| 征信特征表 | recent_3m_query_count | 近 3 个月查询次数 | 整数 |
| 征信特征表 | debt_ratio | 负债比率 | 浮点数 |
二、完整建模流程
1. 环境准备
首先导入建模所需的 Python 库,包括数据处理、可视化、模型训练与评估相关工具。
# 基础数据处理库
import pandas as pd
import numpy as np
# 可视化库
import matplotlib.pyplot as plt
import seaborn as sns
plt.rcParams['font.sans-serif'] = ['SimHei'] # 解决中文显示问题
plt.rcParams['axes.unicode_minus'] = False
# 模型相关库
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression # 基础风控模型
from sklearn.ensemble import RandomForestClassifier # 集成模型
from sklearn.metrics import (accuracy_score, precision_score, recall_score,
f1_score, roc_auc_score, confusion_matrix, roc_curve)
# 忽略警告
import warnings
warnings.filterwarnings('ignore')
2. 数据加载与预处理
2.1 数据加载
假设数据集以 CSV 格式存储,通过pandas加载两张核心表,并通过user_id进行关联,形成完整的建模数据集。
# 加载数据(需替换为实际文件路径)
base_df = pd.read_csv("借款人基础信息表.csv")
credit_df = pd.read_csv("征信特征表.csv")
# 数据关联(基于user_id一对一匹配)
df = pd.merge(base_df, credit_df, on="user_id", how="inner")
# 查看数据基本信息
print("数据集形状:", df.shape)
print("\n前5行数据:")
print(df.head())
print("\n数据类型:")
print(df.dtypes)
print("\n缺失值统计:")
print(df.isnull().sum())
2.2 数据预处理
处理缺失值、异常值与重复值,确保数据质量满足建模要求。
# 1. 处理重复值
df = df.drop_duplicates(subset="user_id", keep="first") # 按user_id去重,保留第一条
print(f"去重后数据集形状:{df.shape}")
# 2. 处理缺失值(根据征信数据特性选择策略)
# 数值型特征:用中位数填充(抗异常值)
num_cols = df.select_dtypes(include=[np.int64, np.float64]).columns.drop("target")
for col in num_cols:
df[col].fillna(df[col].median(), inplace=True)
# 3. 处理异常值(基于3σ原则识别并修正)
for col in num_cols:
mean_val = df[col].mean()
std_val = df[col].std()
# 识别异常值(超出均值±3倍标准差)
upper_bound = mean_val + 3 * std_val
lower_bound = mean_val - 3 * std_val
# 用边界值替换异常值
df[col] = np.where(df[col] > upper_bound, upper_bound, df[col])
df[col] = np.where(df[col] < lower_bound, lower_bound, df[col])
# 4. 确认预处理后的数据质量
print("\n预处理后缺失值统计:")
print(df.isnull().sum())
3. 特征工程(基础版:组件合并)
本案例先采用 “粗暴合并” 策略 —— 将征信特征表中的所有数值型特征直接作为建模变量,暂不做复杂衍生,后续再提出优化方向。
# 1. 划分特征与标签
X = df.drop(["user_id", "target"], axis=1) # 特征:排除唯一标识与标签
y = df["target"] # 标签:违约与否
# 2. 特征类型划分(用于后续预处理 pipeline)
numeric_features = X.select_dtypes(include=[np.int64, np.float64]).columns.tolist()
categorical_features = X.select_dtypes(include=[object]).columns.tolist()
print(f"数值型特征:{numeric_features}")
print(f"分类型特征:{categorical_features}")
# 3. 特征预处理 pipeline(避免数据泄露)
# 数值型特征:标准化(消除量纲影响)
# 分类型特征:独热编码(若存在,如借款人职业等)
preprocessor = ColumnTransformer(
transformers=[
("num", StandardScaler(), numeric_features),
("cat", OneHotEncoder(drop="first", sparse_output=False), categorical_features)
])
# 查看处理后的特征维度
X_processed = preprocessor.fit_transform(X)
print(f"\n预处理后特征维度:{X_processed.shape}")
4. 模型训练与评估
4.1 数据划分
将数据集按 7:3 比例划分为训练集与测试集,确保模型在 unseen 数据上的泛化能力。
X_train, X_test, y_train, y_test = train_test_split(
X_processed, y, test_size=0.3, random_state=42, stratify=y # stratify=y:保持标签分布一致
)
print(f"训练集形状:X_train={X_train.shape}, y_train={y_train.shape}")
print(f"测试集形状:X_test={X_test.shape}, y_test={y_test.shape}")
print(f"训练集违约率:{y_train.sum() / len(y_train):.2%}")
print(f"测试集违约率:{y_test.sum() / len(y_test):.2%}")
4.2 模型训练
选择风控领域常用的逻辑回归(可解释性强)与随机森林(泛化能力强)作为基础模型,对比其性能。
# 1. 定义模型(基于Pipeline整合预处理与训练)
# 逻辑回归模型
lr_model = Pipeline(steps=[
("classifier", LogisticRegression(class_weight="balanced", random_state=42)) # 平衡类别权重
])
# 随机森林模型
rf_model = Pipeline(steps=[
("classifier", RandomForestClassifier(n_estimators=100, class_weight="balanced", random_state=42))
])
# 2. 训练模型
print("开始训练逻辑回归模型...")
lr_model.fit(X_train, y_train)
print("开始训练随机森林模型...")
rf_model.fit(X_train, y_train)
4.3 模型评估
从分类性能(准确率、精确率等)与风控核心指标(AUC、召回率)两个维度评估模型,其中 AUC(ROC 曲线下面积)是风控模型的核心衡量标准(取值 0.5-1,越接近 1 性能越好)。
# 定义评估函数(输出多维度指标)
def evaluate_model(model, X_test, y_test, model_name):
y_pred = model.predict(X_test)
y_pred_proba = model.predict_proba(X_test)[:, 1] # 违约概率预测值
# 计算评估指标
metrics = {
"模型": model_name,
"准确率": accuracy_score(y_test, y_pred).round(4),
"精确率": precision_score(y_test, y_pred).round(4),
"召回率": recall_score(y_test, y_pred).round(4),
"F1分数": f1_score(y_test, y_pred).round(4),
"AUC": roc_auc_score(y_test, y_pred_proba).round(4)
}
# 打印指标
print(f"\n{model_name} 评估结果:")
for key, value in metrics.items():
print(f"{key}: {value}")
# 绘制ROC曲线
fpr, tpr, _ = roc_curve(y_test, y_pred_proba)
plt.plot(fpr, tpr, label=f"{model_name} (AUC={metrics['AUC']})", linewidth=2)
return metrics
# 评估两个模型
lr_metrics = evaluate_model(lr_model, X_test, y_test, "逻辑回归")
rf_metrics = evaluate_model(rf_model, X_test, y_test, "随机森林")
# 绘制ROC曲线(添加基准线)
plt.plot([0, 1], [0, 1], "k--", label="随机猜测 (AUC=0.5)")
plt.xlabel("假正例率(FPR)")
plt.ylabel("真正例率(TPR)")
plt.title("信贷风控模型ROC曲线")
plt.legend()
plt.savefig("风控模型ROC曲线.png", dpi=300, bbox_inches="tight")
plt.show()
# 对比模型性能(表格形式)
metrics_df = pd.DataFrame([lr_metrics, rf_metrics])
print("\n模型性能对比:")
print(metrics_df)
4.4 特征重要性分析(随机森林)
随机森林可输出特征重要性,帮助识别对违约风险影响最大的征信特征,为业务决策提供依据。
# 获取预处理后的特征名称(数值型+独热编码后的分类型)
# 1. 数值型特征名称
num_feature_names = numeric_features
# 2. 分类型特征名称(独热编码后)
cat_encoder = preprocessor.named_transformers_["cat"]
cat_feature_names = []
for i, col in enumerate(categorical_features):
categories = cat_encoder.categories_[i][1:] # drop="first",排除第一个类别
cat_feature_names.extend([f"{col}_{cat}" for cat in categories])
# 3. 完整特征名称列表
feature_names = num_feature_names + cat_feature_names
# 提取随机森林特征重要性
rf_importance = rf_model.named_steps["classifier"].feature_importances_
# 排序并可视化前10个重要特征
importance_df = pd.DataFrame({
"特征名称": feature_names,
"重要性": rf_importance
}).sort_values("重要性", ascending=False).head(10)
# 绘制特征重要性柱状图
plt.figure(figsize=(12, 6))
sns.barplot(x="重要性", y="特征名称", data=importance_df)
plt.title("随机森林模型 - 前10个重要征信特征")
plt.xlabel("特征重要性")
plt.ylabel("特征名称")
plt.savefig("特征重要性.png", dpi=300, bbox_inches="tight")
plt.show()
print("\n前10个重要特征:")
print(importance_df)
三、特征工程优化方向(进阶挖掘)
基础版模型仅用了 “特征合并”,若要进一步提升模型性能,可从以下方向衍生更丰富的征信特征,挖掘数据深层价值:
1. 统计算子衍生
对同一维度的征信特征计算最大、最小、均值、标准差等统计量,反映借款人的历史行为规律。
- 示例 1:计算过去 12 个月内 “每月逾期次数” 的最大值(
max_overdue_12m)、均值(avg_overdue_12m)。 - 示例 2:计算所有信贷账户 “授信额度” 的标准差(
credit_limit_std),反映账户额度的波动情况。
2. 差值与比率特征
通过差值(变化趋势) 与比率(相对关系) 刻画借款人风险变化。
- 示例 1:计算 “当前负债金额” 与 “6 个月前负债金额” 的差值(
debt_change_6m),判断负债是否快速增加。 - 示例 2:计算 “逾期次数” 与 “信贷账户数量” 的比率(
overdue_account_ratio),反映逾期账户的占比。
3. 时间切片特征
按时间维度拆分征信数据,捕捉近期行为(对当前风险更具预测力)。
- 示例 1:将查询记录拆分为 “近 1 个月查询次数”(
query_1m)、“近 3 个月查询次数”(query_3m)、“近 6 个月查询次数”(query_6m)。 - 示例 2:将逾期记录拆分为 “近 3 个月逾期天数”(
overdue_days_3m)、“3-12 个月逾期天数”(overdue_days_3_12m),对比近期与远期履约情况。
4. 行为序列特征
若征信数据包含时间序列(如每月还款记录),可衍生序列特征:
- 示例 1:计算 “连续正常还款月数”(
continuous_normal_months),反映当前履约稳定性。 - 示例 2:识别 “逾期后还款” 的频率(
overdue_repay_ratio),判断借款人的还款意愿。
四、完整代码总结
将上述流程整合为可直接运行的完整代码,使用者仅需替换数据文件路径即可执行。
# 完整代码:基于征信数据的信贷风控模型
# ======================================
# 1. 环境准备
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import (accuracy_score, precision_score, recall_score,
f1_score, roc_auc_score, confusion_matrix, roc_curve)
import warnings
warnings.filterwarnings('ignore')
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 2. 数据加载与预处理
def load_and_preprocess_data(base_path, credit_path):
# 加载数据
base_df = pd.read_csv(base_path)
credit_df = pd.read_csv(credit_path)
df = pd.merge(base_df, credit_df, on="user_id", how="inner")
# 去重
df = df.drop_duplicates(subset="user_id", keep="first")
# 处理缺失值(数值型用中位数)
num_cols = df.select_dtypes(include=[np.int64, np.float64]).columns.drop("target", errors="ignore")
for col in num_cols:
df[col].fillna(df[col].median(), inplace=True)
# 处理异常值(3σ原则)
for col in num_cols:
mean_val = df[col].mean()
std_val = df[col].std()
upper_bound = mean_val + 3 * std_val
lower_bound = mean_val - 3 * std_val
df[col] = np.where(df[col] > upper_bound, upper_bound, df[col])
df[col] = np.where(df[col] < lower_bound, lower_bound, df[col])
return df
# 3. 特征工程与模型训练
def train_risk_models(df):
# 划分特征与标签
X = df.drop(["user_id", "target"], axis=1)
y = df["target"]
# 特征类型划分
numeric_features = X.select_dtypes(include=[np.int64, np.float64]).columns.tolist()
categorical_features = X.select_dtypes(include=[object]).columns.tolist()
# 预处理 pipeline
preprocessor = ColumnTransformer(
transformers=[
("num", StandardScaler(), numeric_features),
("cat", OneHotEncoder(drop="first", sparse_output=False), categorical_features)
])
# 数据划分
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42, stratify=y
)
# 定义模型
lr_model = Pipeline(steps=[("preprocessor", preprocessor),
("classifier", LogisticRegression(class_weight="balanced", random_state=42))])
rf_model = Pipeline(steps=[("preprocessor", preprocessor),
("classifier", RandomForestClassifier(n_estimators=100, class_weight="balanced", random_state=42))])
# 训练模型
lr_model.fit(X_train, y_train)
rf_model.fit(X_train, y_train)
# 评估模型
def evaluate(model, X_test, y_test, name):
y_pred = model.predict(X_test)
y_pred_proba = model.predict_proba(X_test)[:, 1]
metrics = {
"模型": name,
"准确率": accuracy_score(y_test, y_pred).round(4),
"精确率": precision_score(y_test, y_pred).round(4),
"召回率": recall_score(y_test, y_pred).round(4),
"F1": f1_score(y_test, y_pred).round(4),
"AUC": roc_auc_score(y_test, y_pred_proba).round(4)
}
return metrics, y_pred_proba
lr_metrics, lr_proba = evaluate(lr_model, X_test, y_test, "逻辑回归")
rf_metrics, rf_proba = evaluate(rf_model, X_test, y_test, "随机森林")
# 绘制ROC曲线
plt.figure(figsize=(10, 6))
fpr_lr, tpr_lr, _ = roc_curve(y_test, lr_proba)
fpr_rf, tpr_rf, _ = roc_curve(y_test, rf_proba)
plt.plot(fpr_lr, tpr_lr, label=f"逻辑回归 (AUC={lr_metrics['AUC']})", linewidth=2)
plt.plot(fpr_rf, tpr_rf, label=f"随机森林 (AUC={rf_metrics['AUC']})", linewidth=2)
plt.plot([0, 1], [0, 1], "k--", label="随机猜测")
plt.xlabel("假正例率(FPR)")
plt.ylabel("真正例率(TPR)")
plt.title("信贷风控模型ROC曲线")
plt.legend()
plt.savefig("风控模型ROC曲线.png", dpi=300, bbox_inches="tight")
plt.close()
# 输出结果
metrics_df = pd.DataFrame([lr_metrics, rf_metrics])
print("模型性能对比:")
print(metrics_df)
return lr_model, rf_model, metrics_df
# 4. 主函数(执行流程)
if __name__ == "__main__":
# 替换为实际数据路径
BASE_PATH = "借款人基础信息表.csv"
CREDIT_PATH = "征信特征表.csv"
# 执行流程
print("1. 数据加载与预处理...")
df = load_and_preprocess_data(BASE_PATH, CREDIT_PATH)
print(f"预处理后数据集形状:{df.shape}")
print("\n2. 模型训练与评估...")
lr_model, rf_model, metrics_df = train_risk_models(df)
print("\n3. 模型保存(可选)...")
# 若需保存模型,可使用joblib
# import joblib
# joblib.dump(rf_model, "信贷风控随机森林模型.pkl")
print("流程执行完成!")
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐



所有评论(0)