知识点:

  1. 异常值的处理一-箱线图去除异常值的思想和迭代问题
  2. 数据集的划分
  3. 机器学习的流程顺序-----不要数据泄露(归一化器在划分数据集后)
  4. 机器学习模型建模的三行代码
  5. 机器学习模型分类问题的评估
  6. 如何理解分类报告

作业:尝试对心脏病数据集采用机器学习模型建模和评估

1.1导入所需库和包

import pandas as pd
import pandas as pd    #用于数据处理和分析,可处理表格数据。
import numpy as np     #用于数值计算,提供了高效的数组操作。
import matplotlib.pyplot as plt    #用于绘制各种类型的图表
import seaborn as sns   #基于matplotlib的高级绘图库,能绘制更美观的统计图形。
import warnings
warnings.filterwarnings('ignore')
 
 # 设置中文字体(解决中文显示问题)
plt.rcParams['font.sans-serif'] = ['SimHei']  # Windows系统常用黑体字体
plt.rcParams['axes.unicode_minus'] = False    # 正常显示负号

1.2 查看数据信息

data = pd.read_csv(r'../python60-days-challenge-master/heart.csv')    #读取数据
data.head()

运行结果:
在这里插入图片描述

1.3 特征名映射

feature_name_mapping = {
    'age': '年龄',
    'sex': '性别',
    'cp': '胸痛类型',
    'trestbps': '静息血压',
    'chol': '血清胆固醇',
    'fbs': '空腹血糖',
    'restecg': '静息心电图结果',
    'thalach': '最大心率',
    'exang': '运动诱发心绞痛',
    'oldpeak': '运动引起的ST段压低',
    'slope': '运动峰值ST段坡度',
    'ca': '荧光染色血管数',
    'thal': '地中海贫血',
    'target': '是否患病'
}
# 重命名数据框的列名
data = data.rename(columns=feature_name_mapping)
data.head()
data.info()

运行结果:
在这里插入图片描述
在这里插入图片描述
发现数据没有缺失值,所以不需要进行缺失值处理

1.4 异常值处理

continuous_features_cn = ['年龄','静息血压','血清胆固醇','最大心率','运动引起的ST段压低']
# 计算需要多少行和列
n_features = len(continuous_features_cn)
n_cols =3  # 每行显示3个图
n_rows = (n_features + n_cols - 1) // n_cols  # 向上取整计算需要的行数
#创建子图
fig, axes = plt.subplots(n_rows, n_cols, figsize=(15, n_rows * 4))
axes = axes.flatten()# 将axes展平为一维数组,方便使用索引访问
for index, feature in enumerate(continuous_features_cn):
    # 在第index个子图上绘制
    sns.boxplot(x=data[feature], ax=axes[index], color='skyblue')
    axes[index].set_title(f'{feature}的分布', fontsize=12, fontweight='bold')
    axes[index].set_xlabel(feature, fontsize=10)
    # 隐藏多余的空白子图
for i in range(n_features, len(axes)):
    axes[i].set_visible(False)
plt.tight_layout()
plt.rcParams['figure.dpi'] = 300#指定分辨率
plt.show()

运行结果:
在这里插入图片描述
主要就是后四个连续特征有异常值,下面我们选择异常值多的“静息血压”和“血清胆固醇”进行异常值处理

column_name = ['静息血压','血清胆固醇']

# 1. 计算 Q1, Q3 和 IQR
for i in column_name:
    Q1 = data[i].quantile(0.25)#计算分位点
    Q3 = data[i].quantile(0.75)
    IQR = Q3 - Q1

# 2. 确定异常值边界 (使用 1.5 倍 IQR)
    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR#确定上界和下界

    print(f"--- '{i}' 异常值处理信息 ---")
    print(f"Q1 (25th Percentile): {Q1:.2f}")
    print(f"Q3 (75th Percentile): {Q3:.2f}")
    print(f"IQR (Q3 - Q1): {IQR:.2f}")
    print(f"下限 (Lower Bound): {lower_bound:.2f}")
    print(f"上限 (Upper Bound): {upper_bound:.2f}")

# 3. 筛选数据:保留在边界内的数据
    data_before_drop = len(data)
    data_filtered = data[
    (data[i] >= lower_bound) & 
    (data[i] <= upper_bound)
    ].copy() # 使用 .copy() 避免 SettingWithCopyWarning

# 4. 更新 DataFrame 并报告结果
    data = data_filtered
    data_after_drop = len(data)
    rows_dropped = data_before_drop - data_after_drop

    print(f"\n原始数据行数: {data_before_drop}")
    print(f"删除异常值后行数: {data_after_drop}")
    print(f"共删除异常值 (行): {rows_dropped}")

print(f"\nDataFrame 已更新,'{column_name}' 的异常值已被移除。")

运行结果:
在这里插入图片描述

continuous_features_cn = ['静息血压','血清胆固醇']
# 计算需要多少行和列
n_features = len(continuous_features_cn)
n_cols =2  # 每行显示2个图
n_rows = (n_features + n_cols - 1) // n_cols  # 向上取整计算需要的行数
#创建子图
fig, axes = plt.subplots(n_rows, n_cols, figsize=(15, n_rows * 4))
axes = axes.flatten()# 将axes展平为一维数组,方便使用索引访问
for index, feature in enumerate(continuous_features_cn):
    # 在第index个子图上绘制
    sns.boxplot(x=data[feature], ax=axes[index], color='skyblue')
    axes[index].set_title(f'{feature}的分布', fontsize=12, fontweight='bold')
    axes[index].set_xlabel(feature, fontsize=10)
    # 隐藏多余的空白子图
for i in range(n_features, len(axes)):
    axes[i].set_visible(False)
plt.tight_layout()
plt.rcParams['figure.dpi'] = 300#指定分辨率
plt.show()

运行结果:
在这里插入图片描述
但是其实处理异常值之后画箱线图的话,因为你的异常值删去了,他继续画图应该还会出现异常值,因为他是基于新的值进行绘制的,但是可能是因为先前的异常值出现了断层(太大或者太小了),这是幸运的情况,但是平时其实还是会有异常值的,所以可以利用showfliers=False,强制不显示异常值。

二.机器学习建模

2.1数据划分

data.columns#这是属性

运行结果:
在这里插入图片描述

# 划分训练集和测试集 
from sklearn.model_selection import train_test_split
X = data.drop(['是否患病'], axis=1)  # 特征,axis=1表示按列删除
y = data['是否患病']  # 标签
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) #随机种子固定了就可以划分固定,不是每次随机划分了
 # 划分数据集,20%作为测试集,随机种子为42,train_test_split函数会返回四个结果:X_train训练集特征,X_test测试集特征,y_train训练集标签和y_test测试集标签
# 训练集和测试集的形状
print(f"训练集形状: {X_train.shape}, 测试集形状: {X_test.shape}")  # 打印训练集和测试集的形状

在这里插入图片描述

2.2数据归一化

  • 只需要对连续特征归一化即可,离散特征编码后虽然是数值,但是不用动
from sklearn.model_selection import train_test_split 
from sklearn.preprocessing import StandardScaler, MinMaxScaler#preprocessing预处理文件夹

# ----------------------------------------------------------------------
# 定义连续特征列 (需要归一化的特征)
# ----------------------------------------------------------------------
continuous_features = [
    '年龄','静息血压','血清胆固醇','最大心率','运动引起的ST段压低'
]

# 初始化归一化器 (MinMaxScaler)
scaler = MinMaxScaler() 


# 仅在训练集上 fit (学习最大值和最小值)
# 然后对训练集进行 transform (应用缩放)
# 注意:Scikit-learn 返回 NumPy 数组,需要重新赋值给 DataFrame
X_train[continuous_features] = scaler.fit_transform(X_train[continuous_features])

# 使用训练集学到的参数 (scaler) 直接对测试集进行 transform
# 绝对不能对测试集使用 fit_transform()
X_test[continuous_features] = scaler.transform(X_test[continuous_features])#scaler这里已经训练好了,把他运用给测试集
X_test

运行结果:
在这里插入图片描述

2.3 模型训练与评估

#因为这里是做分类任务,所以导入分类器,其实还有SVC回归器等等
from sklearn.svm import SVC #支持向量机分类器
from sklearn.neighbors import KNeighborsClassifier #K近邻分类器
from sklearn.linear_model import LogisticRegression #逻辑回归分类器
import xgboost as xgb #XGBoost分类器
import lightgbm as lgb #LightGBM分类器
from sklearn.ensemble import RandomForestClassifier #随机森林分类器
from catboost import CatBoostClassifier #CatBoost分类器
from sklearn.tree import DecisionTreeClassifier #决策树分类器
from sklearn.naive_bayes import GaussianNB #高斯朴素贝叶斯分类器
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score # 用于评估分类器性能的指标
from sklearn.metrics import classification_report, confusion_matrix #用于生成分类报告和混淆矩阵
# SVM
svm_model = SVC(random_state=42)#实例化一个支持向量机
svm_model.fit(X_train, y_train)#带入训练数据做拟合
svm_pred = svm_model.predict(X_test)#对测试集特征做预测
print("SVM 混淆矩阵:")
print(confusion_matrix(y_test, svm_pred))  # 打印混淆矩阵
svm_cm = confusion_matrix(y_test, svm_pred)
# 3. 使用 Seaborn 绘制热力图
# annot=True: 把数字显示在格子里
# fmt='d': (decimal) 必须加这个!确保显示整数(123),而不是科学计数法(1.23e2)
# cmap='Blues': 使用蓝色系渐变,越深代表数值越大
# cbar=False: 如果不想旁边有个颜色条,可以关掉
sns.heatmap(svm_cm, annot=True, fmt='d', cmap='Blues', cbar=False ,annot_kws={"size": 16})
# 4. 加上标签(防止看混哪个是真实,哪个是预测)
plt.title('SVM 混淆矩阵', fontsize=15)
plt.xlabel('预测结果 (Predicted)', fontsize=12)
plt.ylabel('真实结果 (Actual)', fontsize=12)
plt.tight_layout()
plt.show()

print("\nSVM 分类报告:")
print(classification_report(y_test, svm_pred))  # 打印分类报告,基于混淆矩阵得到的

# 计算 SVM 评估指标,这些指标默认计算正类的性能
svm_accuracy = accuracy_score(y_test, svm_pred)
svm_precision = precision_score(y_test, svm_pred)
svm_recall = recall_score(y_test, svm_pred)
svm_f1 = f1_score(y_test, svm_pred)#这些指标其实在分类报告里面也有,你可以用函数实现,也可以直接看分类报告
print("SVM 模型评估指标:")
print(f"准确率: {svm_accuracy:.4f}")
print(f"精确率: {svm_precision:.4f}")
print(f"召回率: {svm_recall:.4f}")
print(f"F1 值: {svm_f1:.4f}")

运行结果:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
后续只写出混淆矩阵数值,不进行可视化,若要进行可视化参考上述代码即可

# KNN
knn_model = KNeighborsClassifier()
knn_model.fit(X_train, y_train)
knn_pred = knn_model.predict(X_test)
print("KNN 混淆矩阵:")
print(confusion_matrix(y_test, knn_pred))
print("\nKNN 分类报告:")
print(classification_report(y_test, knn_pred))


knn_accuracy = accuracy_score(y_test, knn_pred)
knn_precision = precision_score(y_test, knn_pred)
knn_recall = recall_score(y_test, knn_pred)
knn_f1 = f1_score(y_test, knn_pred)
print("KNN 模型评估指标:")
print(f"准确率: {knn_accuracy:.4f}")
print(f"精确率: {knn_precision:.4f}")
print(f"召回率: {knn_recall:.4f}")
print(f"F1 值: {knn_f1:.4f}")

运行结果:
在这里插入图片描述

# 逻辑回归
logreg_model = LogisticRegression(random_state=42)
logreg_model.fit(X_train, y_train)
logreg_pred = logreg_model.predict(X_test)
print("逻辑回归 混淆矩阵:")
print(confusion_matrix(y_test, logreg_pred))
print("\n逻辑回归 分类报告:")
print(classification_report(y_test, logreg_pred))


logreg_accuracy = accuracy_score(y_test, logreg_pred)
logreg_precision = precision_score(y_test, logreg_pred)
logreg_recall = recall_score(y_test, logreg_pred)
logreg_f1 = f1_score(y_test, logreg_pred)
print("逻辑回归 模型评估指标:")
print(f"准确率: {logreg_accuracy:.4f}")
print(f"精确率: {logreg_precision:.4f}")
print(f"召回率: {logreg_recall:.4f}")
print(f"F1 值: {logreg_f1:.4f}")

运行结果:
在这里插入图片描述

# 朴素贝叶斯
nb_model = GaussianNB()
nb_model.fit(X_train, y_train)
nb_pred = nb_model.predict(X_test)
print("朴素贝叶斯 混淆矩阵:")
print(confusion_matrix(y_test, nb_pred))

print("\n朴素贝叶斯 分类报告:")
print(classification_report(y_test, nb_pred))

nb_accuracy = accuracy_score(y_test, nb_pred)
nb_precision = precision_score(y_test, nb_pred)
nb_recall = recall_score(y_test, nb_pred)
nb_f1 = f1_score(y_test, nb_pred)
print("朴素贝叶斯 模型评估指标:")
print(f"准确率: {nb_accuracy:.4f}")
print(f"精确率: {nb_precision:.4f}")
print(f"召回率: {nb_recall:.4f}")
print(f"F1 值: {nb_f1:.4f}")

运行结果:
在这里插入图片描述

# 决策树
dt_model = DecisionTreeClassifier(random_state=42)
dt_model.fit(X_train, y_train)
dt_pred = dt_model.predict(X_test)
print("决策树 混淆矩阵:")
print(confusion_matrix(y_test, dt_pred))
print("\n决策树 分类报告:")
print(classification_report(y_test, dt_pred))


dt_accuracy = accuracy_score(y_test, dt_pred)
dt_precision = precision_score(y_test, dt_pred)
dt_recall = recall_score(y_test, dt_pred)
dt_f1 = f1_score(y_test, dt_pred)
print("决策树 模型评估指标:")
print(f"准确率: {dt_accuracy:.4f}")
print(f"精确率: {dt_precision:.4f}")
print(f"召回率: {dt_recall:.4f}")
print(f"F1 值: {dt_f1:.4f}")

运行结果:
在这里插入图片描述

# 随机森林
rf_model = RandomForestClassifier(random_state=42)
rf_model.fit(X_train, y_train)
rf_pred = rf_model.predict(X_test)
print("随机森林 混淆矩阵:")
print(confusion_matrix(y_test, rf_pred))
print("\n随机森林 分类报告:")
print(classification_report(y_test, rf_pred))


rf_accuracy = accuracy_score(y_test, rf_pred)
rf_precision = precision_score(y_test, rf_pred)
rf_recall = recall_score(y_test, rf_pred)
rf_f1 = f1_score(y_test, rf_pred)
print("随机森林 模型评估指标:")
print(f"准确率: {rf_accuracy:.4f}")
print(f"精确率: {rf_precision:.4f}")
print(f"召回率: {rf_recall:.4f}")
print(f"F1 值: {rf_f1:.4f}")

运行结果:
在这里插入图片描述

# XGBoost
xgb_model = xgb.XGBClassifier(random_state=42)
xgb_model.fit(X_train, y_train)
xgb_pred = xgb_model.predict(X_test)
print("XGBoost 混淆矩阵:")
print(confusion_matrix(y_test, xgb_pred))

print("\nXGBoost 分类报告:")
print(classification_report(y_test, xgb_pred))

xgb_accuracy = accuracy_score(y_test, xgb_pred)
xgb_precision = precision_score(y_test, xgb_pred)
xgb_recall = recall_score(y_test, xgb_pred)
xgb_f1 = f1_score(y_test, xgb_pred)
print("XGBoost 模型评估指标:")
print(f"准确率: {xgb_accuracy:.4f}")
print(f"精确率: {xgb_precision:.4f}")
print(f"召回率: {xgb_recall:.4f}")
print(f"F1 值: {xgb_f1:.4f}")

运行结果:
在这里插入图片描述

# LightGBM
lgb_model = lgb.LGBMClassifier(random_state=42)
lgb_model.fit(X_train, y_train)
lgb_pred = lgb_model.predict(X_test)

print("\nLightGBM 分类报告:")
print(classification_report(y_test, lgb_pred))
print("LightGBM 混淆矩阵:")
print(confusion_matrix(y_test, lgb_pred))

lgb_accuracy = accuracy_score(y_test, lgb_pred)
lgb_precision = precision_score(y_test, lgb_pred)
lgb_recall = recall_score(y_test, lgb_pred)
lgb_f1 = f1_score(y_test, lgb_pred)
print("LightGBM 模型评估指标:")
print(f"准确率: {lgb_accuracy:.4f}")
print(f"精确率: {lgb_precision:.4f}")
print(f"召回率: {lgb_recall:.4f}")
print(f"F1 值: {lgb_f1:.4f}")

运行结果:
在这里插入图片描述

反思总结:

  1. 这是整个完整流程的叙述
  2. 但是还缺少了选取最佳模型的步骤,可以通过数学建模的评价任务选取或者根据实际问题分析人为选取
    @浙大疏锦行

更多推荐