使用 XGBoost 分类器进行多分类任务

部署运行你感兴趣的模型镜像

代码解读与逐行注释

1. 导入必要的库
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, OneHotEncoder, LabelEncoder
from sklearn.compose import ColumnTransformer
from xgboost import XGBClassifier
from sklearn.metrics import accuracy_score, classification_report
from matplotlib import pyplot as plt
# 添加SMOTE相关导入
from imblearn.over_sampling import SMOTE
from collections import Counter
  • 目的:导入数据分析、机器学习和可视化所需的库。

  • 关键点
    • pandasnumpy 用于数据处理。

    • train_test_split 用于数据集分割。

    • StandardScalerOneHotEncoder 用于特征预处理。

    • XGBClassifier 是 XGBoost 分类器。

    • SMOTE 用于处理类别不平衡问题。

2. 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']  # 指定默认字体为黑体
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题
  • 目的:确保 Matplotlib 图表中正确显示中文和负号。

3. 定义异常值处理函数
def handle_outliers(df, num_cols):
    """处理异常值"""
    medical_limits = {
        'HGB': (30, 200),  # 血红蛋白范围
        'PLT': (10, 1000),  # 血小板范围
        'PT': (5, 50),  # 凝血酶原时间范围
        'TT': (5, 100),  # 凝血时间范围
        'FIB': (0.5, 10),  # 纤维蛋白原范围
    }
    for col in num_cols:
        if col in medical_limits:
            min_val, max_val = medical_limits[col]
            df[col] = df[col].clip(min_val, max_val)  # 限制范围
    return df
  • 目的:对数值列的异常值进行裁剪,确保数据在医学合理范围内。

  • 关键点
    • 使用 clip 方法将数值限制在指定范围内。

    • 适用于医学指标(如血红蛋白、血小板等)。

4. 读取数据
file_path = r"D:\1-2025年项目\11-血库信息管理系统\Data\test_data1.xls"
data = pd.read_excel(file_path)  # 读取Excel文件
  • 目的:从指定路径读取 Excel 文件。

5. 数据预处理
# 1. 删除无关列
to_drop = ['住院号', '申请医师', '产品码', '献血码', '申请日期','血量']
data = data.drop([col for col in to_drop if col in data.columns], axis=1)

# 合并血液名称类别
data['血液名称'] = data['血液名称'].replace({
    '病毒灭活冰冻血浆': '病毒灭活新鲜冰冻血浆'
})

# 2. 转换数值列和处理缺失值
num_cols = ['HGB', 'PLT', 'PT', 'TT', 'FIB']
for col in num_cols:
    data[col] = pd.to_numeric(data[col], errors='coerce')
    # 使用中位数填充缺失值
    data[col] = data[col].fillna(data[col].median())

# 3. 处理异常值
data = handle_outliers(data, num_cols)
  • 目的:清理数据,包括删除无关列、合并类别、处理缺失值和异常值。

  • 关键点
    • 删除无关列(如住院号、申请医师等)。

    • 合并相似的血液名称类别。

    • 将数值列转换为数值类型,并用中位数填充缺失值。

    • 调用 handle_outliers 函数裁剪异常值。

6. 特征工程
# 1. 基础交互特征
data["HGB_PLT_interaction"] = data["HGB"] * data["PLT"]
data["PT_TT_ratio"] = data["PT"] / data["TT"]  # 凝血相关指标比率
data["FIB_PT_interaction"] = data["FIB"] * data["PT"]  # 纤维蛋白原与凝血酶原时间交互

# 2. 血红蛋白相关特征
data['HGB_risk'] = pd.cut(data['HGB'], 
    bins=[-float('inf'), 70, 100, float('inf')],
    labels=['high_risk', 'medium_risk', 'low_risk'])
data['HGB_risk'] = data['HGB_risk'].map({'high_risk': 2, 'medium_risk': 1, 'low_risk': 0})

# 3. 血小板相关特征
data['PLT_risk'] = pd.cut(data['PLT'], 
    bins=[-float('inf'), 50, 80, 100, float('inf')],
    labels=['very_high_risk', 'high_risk', 'medium_risk', 'low_risk'])
data['PLT_risk'] = data['PLT_risk'].map({
    'very_high_risk': 3, 'high_risk': 2, 'medium_risk': 1, 'low_risk': 0})

# 4. 凝血功能相关特征
data['PT_risk'] = (data['PT'] > data['PT'].mean() * 1.5).astype(int)
data['FIB_risk'] = pd.cut(data['FIB'], 
    bins=[-float('inf'), 1.0, 1.5, 2.0, float('inf')],
    labels=['high_risk', 'medium_risk', 'low_risk', 'normal'])
data['FIB_risk'] = data['FIB_risk'].map({
    'high_risk': 3, 'medium_risk': 2, 'low_risk': 1, 'normal': 0})

# 5. 复合特征
data["HGB_PLT_ratio"] = data["HGB"] / data["PLT"]  # 血红蛋白与血小板比值
data["PT_FIB_interaction"] = data["PT"] * data["FIB"]  # 凝血时间与纤维蛋白原交互
data["Coagulation_index"] = (data["PT"] * data["TT"]) / data["FIB"]  # 凝血综合指数
  • 目的:通过特征工程创建新的特征,增强模型的预测能力。

  • 关键点
    • 创建交互特征(如 HGB_PLT_interaction)。

    • 使用 pd.cut 对数值列进行分箱,并映射为风险等级。

    • 创建复合特征(如 Coagulation_index)。

7. 打印血液名称分布
print("血液名称分布:")
print(data['血液名称'].value_counts())

# 移除样本数过少的类别(少于2个样本)
min_samples = 2
value_counts = data['血液名称'].value_counts()
valid_labels = value_counts[value_counts >= min_samples].index
data = data[data['血液名称'].isin(valid_labels)]

print("\n处理后的血液名称分布:")
print(data['血液名称'].value_counts())
  • 目的:统计血液名称的分布,并移除样本数过少的类别。

8. 数据集分割
X = data.drop('血液名称', axis=1)
y = data['血液名称']

# 使用分层抽样进行分割
X_train, X_test, y_train, y_test = train_test_split(
    X, y, 
    test_size=0.2, 
    random_state=42,
    stratify=y  # 添加分层抽样
)
  • 目的:将数据集分为训练集和测试集,保持类别分布一致。

  • 关键点
    • stratify=y 确保训练集和测试集的类别分布一致。

9. 标签编码
# 然后对训练集和测试集分别进行标签编码
le = LabelEncoder()
y_encoded_train = le.fit_transform(y_train)
y_encoded_test = le.transform(y_test)
  • 目的:将类别标签转换为整数编码。

10. 特征预处理
numeric_features = [
    "HGB", "PLT", "PT", "TT", "FIB",
    "HGB_PLT_interaction", "PT_TT_ratio", "FIB_PT_interaction",
    "HGB_risk", "PLT_risk", "PT_risk", "FIB_risk",
    "HGB_PLT_ratio", "PT_FIB_interaction", "Coagulation_index"
]
categorical_features = ["血型", "科室", "临床诊断", "申请类型"]

preprocessor = ColumnTransformer(
    transformers=[
        ("num", StandardScaler(), numeric_features),
        ("cat", OneHotEncoder(handle_unknown='ignore', sparse_output=False), categorical_features),
    ]
)

# 先进行特征处理
X_train_processed = preprocessor.fit_transform(X_train)
X_test_processed = preprocessor.transform(X_test)
  • 目的:对数值特征进行标准化,对类别特征进行独热编码。

  • 关键点
    • StandardScaler 用于数值特征的标准化。

    • OneHotEncoder 用于类别特征的独热编码。

11. 应用 SMOTE 处理类别不平衡
print("应用SMOTE前的类别分布:")
print(Counter(y_train))

smote = SMOTE(random_state=42, sampling_strategy={
    '病毒灭活新鲜冰冻血浆': 341  # 修改为新的类别名称,样本数与去白细胞悬浮红细胞相同
})

# 对处理后的训练集进行过采样
X_train_resampled, y_train_resampled = smote.fit_resample(X_train_processed, y_train)

print("\n应用SMOTE后的类别分布:")
print(Counter(y_train_resampled))
  • 目的:通过 SMOTE 算法对少数类别进行过采样,解决类别不平衡问题。

12. 定义并训练模型
model = XGBClassifier(
    objective="multi:softmax",
    num_class=len(le.classes_),
    max_depth=6,
    learning_rate=0.03,
    n_estimators=300,
    min_child_weight=5,
    subsample=0.7,
    colsample_bytree=0.7,
    gamma=0.1,
    reg_alpha=0.1,
    reg_lambda=1,
    eval_metric="mlogloss",
    early_stopping_rounds=30,
    random_state=42
)

# 训练模型
model.fit(
    X_train_resampled,
    y_encoded_train,
    eval_set=[(X_test_processed, y_encoded_test)],
    verbose=True
)
  • 目的:使用 XGBoost 分类器训练模型。

  • 关键点
    • objective="multi:softmax" 表示多分类任务。

    • num_class 指定类别数量。

    • early_stopping_rounds 用于防止过拟合。

13. 模型评估
y_pred_encoded = model.predict(X_test_processed)
y_pred_proba = model.predict_proba(X_test_processed)
y_pred = le.inverse_transform(y_pred_encoded)

# 获取类别数量并确保标签编码一致
n_classes = len(le.classes_)

# 修正标签二值化过程
y_test_bin = np.zeros((len(y_encoded_test), n_classes))
y_pred_bin = np.zeros((len(y_pred_encoded), n_classes))

for i in range(n_classes):
    y_test_bin[:, i] = (y_encoded_test == i).astype(int)
    y_pred_bin[:, i] = (y_pred_encoded == i).astype(int)

# 计算每个类别的ROC曲线和AUC
fpr = dict()
tpr = dict()
roc_auc = dict()

for i in range(n_classes):
    fpr[i], tpr[i], _ = roc_curve(y_test_bin[:, i], y_pred_proba[:, i])
    roc_auc[i] = auc(fpr[i], tpr[i])
  • 目的:对模型进行评估,包括准确率、F1 分数、ROC 曲线等。

14. 混淆矩阵可视化
plt.figure(figsize=(10, 8))
cm = confusion_matrix(y_test, y_pred)
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
            xticklabels=le.classes_,
            yticklabels=le.classes_)
plt.title('混淆矩阵')
plt.xlabel('预测标签')
plt.ylabel('真实标签')
plt.tight_layout()
plt.show()
  • 目的:绘制混淆矩阵,直观展示分类结果。

15. 精确率-召回率曲线
plt.figure(figsize=(10, 8))
for i in range(n_classes):
    precision, recall, _ = precision_recall_curve(y_test_bin[:, i], y_pred_proba[:, i])
    avg_precision = average_precision_score(y_test_bin[:, i], y_pred_proba[:, i])
    plt.plot(recall, precision,
             label=f'{le.classes_[i]} (AP = {avg_precision:.2f})')

plt.xlabel('召回率')
plt.ylabel('精确率')
plt.title('各类别精确率-召回率曲线')
plt.legend(loc='lower left')
plt.grid(True)
plt.show()
  • 目的:绘制精确率-召回率曲线,评估模型在不同阈值下的性能。

16. 各类别详细指标
print("\n=== 各类别详细评估指标 ===")
for i, class_name in enumerate(le.classes_):
    print(f"\n{class_name}:")
    print(f"AUC: {roc_auc[i]:.4f}")
    print(f"平均精确率: {average_precision_score(y_test_bin[:, i], y_pred_proba[:, i]):.4f}")
  • 目的:输出每个类别的 AUC 和平均精确率。

17. 绘制 ROC 曲线
plt.figure(figsize=(10, 8))
colors = ['blue', 'red', 'green', 'yellow', 'purple']
for i, color in zip(range(n_classes), colors):
    plt.plot(fpr[i], tpr[i], color=color, lw=2,
             label=f'ROC curve of {le.classes_[i]} (AUC = {roc_auc[i]:.2f})')

plt.plot([0, 1], [0, 1], 'k--', lw=2)
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC Curves for Multi-class Classification')
plt.legend(loc="lower right")
plt.show()
  • 目的:绘制 ROC 曲线,评估模型的分类性能。

18. 打印每个类别的 AUC 值
print("\n各类别的AUC值:")
for i in range(n_classes):
    print(f"{le.classes_[i]}: {roc_auc[i]:.4f}")
  • 目的:输出每个类别的 AUC 值,进一步评估模型性能。

总结

这段代码实现了一个完整的机器学习流程,包括数据预处理、特征工程、模型训练和评估。通过 SMOTE 处理类别不平衡问题,并使用 XGBoost 分类器进行多分类任务。最后,通过多种评估指标(如准确率、F1 分数、ROC 曲线等)对模型性能进行了全面分析。

您可能感兴趣的与本文相关的镜像

Yolo-v8.3

Yolo-v8.3

Yolo

YOLO(You Only Look Once)是一种流行的物体检测和图像分割模型,由华盛顿大学的Joseph Redmon 和Ali Farhadi 开发。 YOLO 于2015 年推出,因其高速和高精度而广受欢迎

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值