背景
虽然折线图能够直观地展示不同数据集在各个指标上的表现,但它存在一个明显的不足:无法展示指标的 置信区间 和结果的波动范围,这使得无法判断模型评价指标的 稳定性 和 不确定性 ,从而可能掩盖真实的表现差异,如果指标的置信区间较大,单一的得分点并不能充分反映模型的可靠性
当将指标的 置信区间 与 误差条 结合,可视化呈现时,不仅能直观地展示模型性能,还能帮助评估结果的可靠性与波动范围
代码实现
模型构建
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings("ignore")
plt.rcParams['font.family'] = 'Times New Roman'
plt.rcParams['axes.unicode_minus'] = False
# 读取衍生数据集
df_derivation_cohort = pd.read_csv("2024-12-19公众号Python机器学习AI_data.csv")
# 读取外部验证数据集
df_external_validation_cohort = pd.read_csv("2024-12-19公众号Python机器学习AI_val.csv")
from sklearn.model_selection import train_test_split
# 提取目标变量 y,即“Outcome_Occlusion_MI”列
y = df_derivation_cohort['Outcome_Occlusion_MI']
# 提取特征变量 X,去掉目标变量列
X = df_derivation_cohort.drop('Outcome_Occlusion_MI', axis=1)
# 划分训练集和测试集,20% 为测试集,按 y 的分层抽样
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, stratify=y, random_state=99)
# 提取外部验证集的目标变量 y_val
y_val = df_external_validation_cohort['Outcome_Occlusion_MI']
# 提取外部验证集的特征变量 X_val
X_val = df_external_validation_cohort.drop('Outcome_Occlusion_MI', axis=1)
from sklearn.ensemble import RandomForestClassifier
from sklearn.calibration import CalibratedClassifierCV
# 创建随机森林分类器
clf = RandomForestClassifier(
class_weight='balanced_subsample', # 每个决策树的子样本内,自动调整每个类别的权重,平衡不平衡数据集
criterion='entropy', # 使用信息增益(熵)作为分裂标准
n_jobs=-1, # 使用所有可用的CPU核心加速训练
random_state=42, # 固定随机种子,确保结果可复现
max_features='log2', # 每次分裂时考虑的最大特征数为 log2(总特征数)
n_estimators=20, # 随机森林中决策树的数量
min_samples_split=0.01, # 内部节点分裂所需的最小样本比例(即至少占总样本1%)
min_samples_leaf=0.005, # 叶子节点所需的最小样本比例(即至少占总样本0.5%)
min_impurity_decrease=1e-2, # 分裂时要求的最小纯度增益,防止过拟合
bootstrap=True, # 使用自助法采样(带放回抽样)构建每棵决策树
ccp_alpha=1e-2, # 剪枝时的复杂度参数,增加模型泛化能力
max_samples=0.75, # 每棵树只使用75%的样本子集(相对总样本数量)
oob_score=True # 使用袋外样本估计模型泛化性能
)
# 对随机森林分类器进行校准
clf = CalibratedClassifierCV(
clf, # 要校准的基础分类器(随机森林)
cv=5, # 使用5折交叉验证来校准分类器
method="isotonic" # 使用保序回归法进行概率校准,适合数据较多的情况
)
clf.fit(X_train, y_train)
通过构建 随机森林分类器 ,对训练集进行拟合、参数优化与 概率校准 ,以提高模型在不平衡数据集上的预测性能,并准备对测试集和外部验证集进行评估
模型评价指标计算
from sklearn.metrics import (
confusion_matrix, accuracy_score, precision_score, recall_score, f1_score,
roc_auc_score, cohen_kappa_score
)
from sklearn.utils import resample
# clf 是已训练的模型
datasets = {
"Training Set": (X_train, y_train),
"Testing Set": (X_test, y_test),
"Validation Set": (X_val, y_val)
}
# 计算训练集、测试集、验证集的指标
results_with_ci = {}
for name, (X, y) in datasets.items():
y_prob = clf.predict_proba(X)[:, 1] # 预测正类的概率
y_pred = clf.predict(X) # 预测标签
results_with_ci[name] = calculate_metrics_with_ci(y, y_pred, y_prob)
# 转换结果为DataFrame
results_with_ci_df = pd.DataFrame(results_with_ci).T
results_with_ci_df
这里的 calculate_metrics_with_ci 是一个自定义函数,用于计算分类模型的多项评价指标(如准确率、精确率、召回率、F1分数等),并通过Bootstrap重抽样方法为这些指标估算出置信区间,代码与数据集获取:如需获取本文的源代码和数据集,请添加作者微信联系
折线图可视化
# 提取主要指标值 (去掉置信区间,只保留均值)
results_clean = results_with_ci_df.applymap(lambda x: float(x.split(' ')[0]))
# 数据转换为长格式
results_long = results_clean.reset_index().melt(id_vars="index", var_name="Dataset", value_name="Score")
results_long.rename(columns={"index": "Metrics"}, inplace=True)
import matplotlib.ticker as ticker
# 数据准备: 宽格式 -> 长格式
def prepare_data(results_clean):
results_long = results_clean.reset_index().melt(
id_vars="index",
var_name="Metrics",
value_name="Score"
)
results_long.rename(columns={"index": "Dataset"}, inplace=True)
return results_long
# 可视化函数
def plot_metrics_long(metrics_long_df, title="Metrics Visualization", save_path=None):
"""
绘制折线图: x轴为指标 (Metrics),图例为数据集 (Dataset)。
Args:
metrics_long_df (DataFrame): 长格式 DataFrame,包含 'Dataset', 'Metrics', 'Score' 三列。
title (str): 图标题。
save_path (str): 可选,若提供路径,则保存图表。
"""
# 获取唯一数据集和指标
datasets = metrics_long_df['Dataset'].unique()
metrics = metrics_long_df['Metrics'].unique()
colors = ['#ff3522', 'brown', 'orange', 'purple', '#0D99D4', '#4fbe5e', 'blue']
plt.figure(figsize=(10, 6), dpi=300)
# 绘制每个数据集的折线图
for idx, dataset in enumerate(datasets):
subset = metrics_long_df[metrics_long_df['Dataset'] == dataset]
plt.plot(subset['Metrics'], subset['Score'], marker='o',
label=dataset, color=colors[idx % len(colors)])
# 设置坐标轴和样式
ax = plt.gca()
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.yaxis.set_major_locator(ticker.MultipleLocator(0.1))
ax.yaxis.set_minor_locator(ticker.MultipleLocator(0.05))
# 图表属性
plt.title(title, fontsize=12)
plt.xlabel("Metrics", fontsize=10)
plt.ylabel("Score", fontsize=10)
plt.ylim(0.0, 1.1)
plt.xticks(rotation=45, ha='right')
plt.legend(title="Dataset", loc='center left', bbox_to_anchor=(1, 0.5), frameon=False)
plt.grid(which='both', linestyle='--', linewidth=0.5, color='gray')
# 保存图表
if save_path:
plt.savefig(save_path, format='pdf', bbox_inches='tight')
plt.tight_layout()
plt.show()
# 使用 results_clean 进行可视化
results_long = prepare_data(results_clean) # 数据转换
plot_metrics_long(results_long, title="Model Performance Across Datasets", save_path="metrics_plot.pdf")
绘制折线图,其中 X 轴为评价指标(如 Accuracy、Precision 等),不同数据集(如训练集、测试集、验证集)在图例中区分,Y 轴显示各指标的分数
绘制训练集置信区间+误差条可视化
import re
# 提取 "Training Set" 行数据
training_set = results_with_ci_df.loc["Training Set"]
# 初始化存储列表
features = training_set.index.tolist()
odds_ratios = []
ci_lower = []
ci_upper = []
# 定义提取函数(使用正则表达式)
def extract_ci(value):
"""提取实际值、置信区间下限和上限"""
match = re.match(r"([\d.]+) \(([\d.]+), ([\d.]+)\)", value)
if match:
return float(match.group(1)), float(match.group(2)), float(match.group(3))
return None, None, None
# 遍历提取数据
for value in training_set:
odds, lower, upper = extract_ci(value)
odds_ratios.append(odds)
ci_lower.append(lower)
ci_upper.append(upper)
# 计算 markersize:值越大,圆圈越小(通过反比例关系)
max_size = 300 # 最大圆圈大小
min_size = 50 # 最小圆圈大小
scaled_markersize = max_size - (odds_ratios / np.max(odds_ratios)) * (max_size - min_size)
# 绘图
fig, ax = plt.subplots(figsize=(8, 6))
y_pos = np.arange(len(features))
# 绘制误差条和动态圆圈
for i in range(len(features)):
ax.errorbar(odds_ratios[i], y_pos[i],
xerr=[[odds_ratios[i] - ci_lower[i]], [ci_upper[i] - odds_ratios[i]]],
fmt='o', color='lightblue', ecolor='lightcoral',
capsize=5, capthick=2, markersize=np.sqrt(scaled_markersize[i]))
# 设置Y轴
ax.set_yticks(y_pos)
ax.set_yticklabels(features)
ax.invert_yaxis() # 标签从上到下读取
# 设置标题和X轴标签
ax.set_xlabel('Evaluation Metrics (95% CI)')
ax.set_title('Model Evaluation Metrics: Random Forest')
plt.axvline(x=1, linestyle='--', color='grey', linewidth=1)
plt.tight_layout()
plt.savefig('Model Evaluation Metrics Random Forest.pdf', format='pdf', bbox_inches='tight', dpi=1200)
plt.show()
提取模型评价指标及其置信区间,并通过误差条和动态圆圈大小直观展示随机森林模型的性能表现。 动态圆圈规则 是根据指标值的大小反比例映射到圆圈的尺寸,即 指标值越大,圆圈越小 ,从而通过圆圈的视觉效果突出显示指标的相对表现或重要性。此外,这里的指标(如准确率、精确率、召回率等) 越接近1,表示模型性能越好
绘制所有数据集置信区间+误差条可视化
# 定义提取函数(使用正则表达式)
def extract_ci(value):
"""提取实际值、置信区间下限和上限"""
match = re.match(r"([\d.]+) \(([\d.]+), ([\d.]+)\)", value)
if match:
return float(match.group(1)), float(match.group(2)), float(match.group(3))
return None, None, None
# 提取每个数据集的指标
datasets = ["Training Set", "Validation Set", "Testing Set"]
colors = ["lightblue", "lightgreen", "lightcoral"] # 不同数据集的颜色
markers = ["o", "s", "D"] # 不同数据集的标记样式
# 初始化存储结果
results = {}
for dataset in datasets:
data = results_with_ci_df.loc[dataset]
odds_ratios, ci_lower, ci_upper = [], [], []
for value in data:
odds, lower, upper = extract_ci(value)
odds_ratios.append(odds)
ci_lower.append(lower)
ci_upper.append(upper)
results[dataset] = {
"features": data.index.tolist(),
"odds_ratios": np.array(odds_ratios),
"ci_lower": np.array(ci_lower),
"ci_upper": np.array(ci_upper)
}
# 绘图
fig, ax = plt.subplots(figsize=(10, 8))
y_pos = np.arange(len(results["Training Set"]["features"]))
# 设置不同数据集的颜色和标记
colors = {"Training Set": "lightblue", "Validation Set": "lightgreen", "Testing Set": "lightcoral"}
markers = {"Training Set": "o", "Validation Set": "s", "Testing Set": "D"}
# 绘制三个数据集的误差条和圆圈
for i, dataset in enumerate(datasets):
data = results[dataset]
scaled_markersize = max_size - (data["odds_ratios"] / np.max(data["odds_ratios"])) * (max_size - min_size)
# 由于是同一指标,y轴位置需要做微小偏移
y_offset = 0.2 * (i - 1) # 偏移量:-0.2, 0, 0.2
for j in range(len(data["features"])):
ax.errorbar(data["odds_ratios"][j], y_pos[j] + y_offset,
xerr=[[data["odds_ratios"][j] - data["ci_lower"][j]],
[data["ci_upper"][j] - data["odds_ratios"][j]]],
fmt=markers[dataset], color=colors[dataset], ecolor=colors[dataset],
capsize=5, capthick=2, markersize=np.sqrt(scaled_markersize[j]))
# 设置Y轴
ax.set_yticks(y_pos)
ax.set_yticklabels(results["Training Set"]["features"]) # 所有数据集的指标一致
ax.invert_yaxis()
# 设置标题和标签
ax.set_xlabel('Evaluation Metrics (95% CI)')
ax.set_title('Model Evaluation Metrics: Random Forest')
# 添加参考线
plt.axvline(x=1, linestyle='--', color='grey', linewidth=1)
# 添加图例
handles = [plt.Line2D([0], [0], color=colors[name], marker=markers[name], linestyle='',
markersize=10, label=name) for name in datasets]
ax.legend(handles=handles, title="Dataset", loc="upper left")
# 美化图表
plt.tight_layout()
plt.savefig('Model_Evaluation_Metrics_Random_Forest_Aligned.pdf', format='pdf', bbox_inches='tight', dpi=1200)
plt.show()
将训练集、验证集和测试集的模型评价指标及其置信区间绘制在同一张图表中,并通过对Y轴的微小偏移(y_offset)将不同数据集的相同指标错开排列,同时使用不同颜色和标记区分数据集,使得不同数据集的指标表现更直观地比较
往期推荐
从模型构建到在线部署:基于Stacking集成模型的全流程实现与SHAP可视化
探究SHAP交互效应:基于shap.dependence_plot与自定义可视化方法的对比分析
利用Optuna TPE算法优化RF模型及3D曲面图展示调参过程
nature medicine二分类结局随机森林模型构建与评估复现
如何用SHAP解读集成学习Stacking中的基学习器和元学习器以及整体模型贡献
从入门到实践:如何利用Stacking集成多种机器学习算法提高模型性能
如果你对类似于这样的文章感兴趣。
欢迎关注、点赞、转发~
个人观点,仅供参考