Scikit-Learn 对糖尿病数据集(回归任务)进行全面分析

发布于:2025-09-14 ⋅ 阅读:(22) ⋅ 点赞:(0)

背景

糖尿病数据集,442个样本,10个特征(年龄、血压等),目标为疾病进展值
该数据集为回归问题,需要使用回归分析方法进行分析

数据集速览

import pandas as pd
from sklearn.datasets import load_diabetes

diabetes = load_diabetes()

# 将数据转换为 Pandas DataFrame 以便更好地查看
# features(特征)
df_features = pd.DataFrame(diabetes.data, columns=diabetes.feature_names)

# target(目标变量)
df_target = pd.DataFrame(diabetes.target, columns=['target'])

# 合并特征和目标变量为一个 DataFrame
df_diabetes = pd.concat([df_features, df_target], axis=1)

print("--- 数据集简要概览 ---")
print(f"数据形状 (行, 列): {df_diabetes.shape}")
print("\n数据集特征名称:")
print(diabetes.feature_names)
print("\n目标变量描述:")
print(diabetes.DESCR.split('\n')[-3]) # 提取目标变量的简要描述

print("\n--- 数据前5行预览 ---")
print(df_diabetes.head())

print("\n--- 数据信息 (类型和非空值) ---")
print(df_diabetes.info())
--- 数据集简要概览 ---
数据形状 (行, 列): (442, 11)

数据集特征名称:
['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6']

目标变量描述:
Bradley Efron, Trevor Hastie, Iain Johnstone and Robert Tibshirani (2004) "Least Angle Regression," Annals of Statistics (with discussion), 407-499.

--- 数据前5行预览 ---
        age       sex       bmi        bp        s1        s2        s3  \
0  0.038076  0.050680  0.061696  0.021872 -0.044223 -0.034821 -0.043401   
1 -0.001882 -0.044642 -0.051474 -0.026328 -0.008449 -0.019163  0.074412   
2  0.085299  0.050680  0.044451 -0.005670 -0.045599 -0.034194 -0.032356   
3 -0.089063 -0.044642 -0.011595 -0.036656  0.012191  0.024991 -0.036038   
4  0.005383 -0.044642 -0.036385  0.021872  0.003935  0.015596  0.008142   

         s4        s5        s6  target  
0 -0.002592  0.019907 -0.017646   151.0  
1 -0.039493 -0.068332 -0.092204    75.0  
2 -0.002592  0.002861 -0.025930   141.0  
3  0.034309  0.022688 -0.009362   206.0  
4 -0.002592 -0.031988 -0.046641   135.0  

--- 数据信息 (类型和非空值) ---
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 442 entries, 0 to 441
Data columns (total 11 columns):
 #   Column  Non-Null Count  Dtype  
---  ------  --------------  -----  
 0   age     442 non-null    float64
 1   sex     442 non-null    float64
 2   bmi     442 non-null    float64
 3   bp      442 non-null    float64
 4   s1      442 non-null    float64
 5   s2      442 non-null    float64
 6   s3      442 non-null    float64
 7   s4      442 non-null    float64
 8   s5      442 non-null    float64
 9   s6      442 non-null    float64
 10  target  442 non-null    float64
dtypes: float64(11)
memory usage: 38.1 KB
None
这份数据集共有 10 个特征
它们都是经过预处理和标准化的浮点数值,具体含义如下:
1. age: 年龄(years)
2. sex: 性别
3. bmi: 身体质量指数(body mass index)
4. bp: 平均血压(average blood pressure)
5. s1: T-细胞(T-cells)
6. s2: 低密度脂蛋白(LDL)
7. s3: 高密度脂蛋白(HDL)
8. s4: 甲状腺刺激素(TSH)
9. s5: 血糖(blood sugar)
10. s6: 淋巴细胞(lymphocytes)

分析方法

  • 线性回归
  • K近邻(KNN)
  • 决策树
  • 支持向量机
  • 随机森林
  • 多层感知器(MLP)
  • Lasso 回归
  • Ridge 回归
  • 梯度提升回归
  • AdaBoost 回归
  • Huber 回归

分析步骤

  1. 加载数据集
  2. 拆分训练集、测试集
  3. 数据预处理(标准化)
  4. 选择模型
  5. 模型训练(拟合)
  6. 测试模型效果
  7. 评估模型

分析结果

不同模型的预测值 vs 真实值效果对比

预测值 vs 真实值

残差图

残差图

特征重要性

特征重要性

预测值分布

预测值分布

不同模型评估明细

--- 数据集概览 ---
数据形状: (442, 10)
特征名称: ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6']
目标变量名称: 糖尿病病程进展

--- 数据划分结果 ---
训练集形状: (309, 10)
测试集形状: (133, 10)

--- 正在对数据进行标准化处理 ---

--- 模型训练与评估 ---

--- 正在训练 线性回归 模型 ---
线性回归 模型的均方误差 (MSE): 2821.7510
线性回归 模型的 R^2 得分: 0.4773

--- 正在训练 K近邻 (K-NN) 模型 ---
K近邻 (K-NN) 模型的均方误差 (MSE): 3277.7368
K近邻 (K-NN) 模型的 R^2 得分: 0.3928

--- 正在训练 决策树 模型 ---
决策树 模型的均方误差 (MSE): 5686.6015
决策树 模型的 R^2 得分: -0.0534

--- 正在训练 支持向量机 (SVR) 模型 ---
支持向量机 (SVR) 模型的均方误差 (MSE): 2822.7007
支持向量机 (SVR) 模型的 R^2 得分: 0.4771

--- 正在训练 随机森林 模型 ---
随机森林 模型的均方误差 (MSE): 2859.3911
随机森林 模型的 R^2 得分: 0.4703

--- 正在训练 多层感知器 (MLP) 模型 ---
/opt/anaconda3/lib/python3.13/site-packages/sklearn/neural_network/_multilayer_perceptron.py:691: ConvergenceWarning: Stochastic Optimizer: Maximum iterations (20000) reached and the optimization hasn't converged yet.
  warnings.warn(
多层感知器 (MLP) 模型的均方误差 (MSE): 4791.4414
多层感知器 (MLP) 模型的 R^2 得分: 0.1124

--- 正在训练 Lasso 回归 模型 ---
Lasso 回归 模型的均方误差 (MSE): 2817.0876
Lasso 回归 模型的 R^2 得分: 0.4782

--- 正在训练 Ridge 回归 模型 ---
Ridge 回归 模型的均方误差 (MSE): 2819.9820
Ridge 回归 模型的 R^2 得分: 0.4776

--- 正在训练 梯度提升回归 模型 ---
梯度提升回归 模型的均方误差 (MSE): 3083.3332
梯度提升回归 模型的 R^2 得分: 0.4288

--- 正在训练 AdaBoost 回归 模型 ---
AdaBoost 回归 模型的均方误差 (MSE): 2874.6206
AdaBoost 回归 模型的 R^2 得分: 0.4675

--- 正在训练 Huber 回归 模型 ---
Huber 回归 模型的均方误差 (MSE): 2828.7736
Huber 回归 模型的 R^2 得分: 0.4760

所有模型的分析已完成。

代码

from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsRegressor
from sklearn.tree import DecisionTreeRegressor
from sklearn.svm import SVR
from sklearn.linear_model import LinearRegression, Lasso, Ridge, HuberRegressor
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor, AdaBoostRegressor
from sklearn.neural_network import MLPRegressor
from sklearn.metrics import mean_squared_error, r2_score
import numpy as np
import matplotlib.pyplot as plt

# 设置 Matplotlib 字体以正确显示中文
plt.rcParams['font.sans-serif'] = ['SimHei', 'WenQuanYi Zen Hei', 'STHeiti', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False 

def perform_diabetes_analysis():
    """
    使用 scikit-learn 对糖尿病数据集进行全面的分析。
    该函数包含数据加载、预处理、模型训练、评估和结果可视化。
    """
    print("--- 正在加载糖尿病数据集 ---")
    diabetes = load_diabetes()
    
    X = diabetes.data
    y = diabetes.target
    feature_names = list(diabetes.feature_names)

    print("\n--- 数据集概览 ---")
    print(f"数据形状: {X.shape}")
    print(f"特征名称: {feature_names}")
    print(f"目标变量名称: {'糖尿病病程进展'}")

    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

    print("\n--- 数据划分结果 ---")
    print(f"训练集形状: {X_train.shape}")
    print(f"测试集形状: {X_test.shape}")
    
    print("\n--- 正在对数据进行标准化处理 ---")
    scaler = StandardScaler()
    X_train_scaled = scaler.fit_transform(X_train)
    X_test_scaled = scaler.transform(X_test)
    
    models = {
        "线性回归": LinearRegression(),
        "K近邻 (K-NN)": KNeighborsRegressor(n_neighbors=5),
        "决策树": DecisionTreeRegressor(random_state=42),
        "支持向量机 (SVR)": SVR(kernel='rbf', C=100),
        "随机森林": RandomForestRegressor(random_state=42),
        "多层感知器 (MLP)": MLPRegressor(random_state=42, max_iter=20000),
        "Lasso 回归": Lasso(alpha=0.1, max_iter=10000),
        "Ridge 回归": Ridge(alpha=1.0, max_iter=10000),
        "梯度提升回归": GradientBoostingRegressor(random_state=42),
        "AdaBoost 回归": AdaBoostRegressor(random_state=42),
        "Huber 回归": HuberRegressor(max_iter=10000)
    }

    predictions = {}
    feature_importances = {}

    print("\n--- 模型训练与评估 ---")
    for name, model in models.items():
        print(f"\n--- 正在训练 {name} 模型 ---")
        model.fit(X_train_scaled, y_train)
        y_pred = model.predict(X_test_scaled)
        
        mse = mean_squared_error(y_test, y_pred)
        r2 = r2_score(y_test, y_pred)
        
        print(f"{name} 模型的均方误差 (MSE): {mse:.4f}")
        print(f"{name} 模型的 R^2 得分: {r2:.4f}")
        
        predictions[name] = y_pred

        if hasattr(model, 'feature_importances_'):
            feature_importances[name] = model.feature_importances_
        
    print("\n所有模型的分析已完成。")

    # --- 预测值 vs. 实际值可视化 ---
    print("\n--- 预测值 vs. 实际值散点图 ---")
    num_models = len(models)
    cols = 3
    rows = (num_models + cols - 1) // cols
    fig, axes = plt.subplots(rows, cols, figsize=(18, 6 * rows))
    axes = axes.flatten()

    for i, name in enumerate(models.keys()):
        ax = axes[i]
        y_pred = predictions[name]
        
        ax.scatter(y_test, y_pred, alpha=0.6, label='预测值')
        lims = [
            np.min([ax.get_xlim(), ax.get_ylim()]),
            np.max([ax.get_xlim(), ax.get_ylim()]),
        ]
        ax.plot(lims, lims, 'k--', alpha=0.75, zorder=0, label='理想预测')
        
        ax.set_title(f'{name} - 预测值 vs. 实际值', fontsize=14)
        ax.set_xlabel('实际病程进展', fontsize=12)
        ax.set_ylabel('预测病程进展', fontsize=12)
        ax.legend()
        ax.grid(True)
    
    for j in range(num_models, len(axes)):
        axes[j].axis('off')

    plt.tight_layout()
    plt.show()

    # --- 残差图可视化 ---
    print("\n--- 残差图可视化 ---")
    fig, axes = plt.subplots(rows, cols, figsize=(18, 6 * rows))
    axes = axes.flatten()
    
    for i, name in enumerate(models.keys()):
        ax = axes[i]
        y_pred = predictions[name]
        residuals = y_test - y_pred
        
        ax.scatter(y_pred, residuals, alpha=0.6)
        ax.axhline(y=0, color='r', linestyle='--', linewidth=2)
        
        ax.set_title(f'{name} - 残差图', fontsize=14)
        ax.set_xlabel('预测值', fontsize=12)
        ax.set_ylabel('残差 (实际值 - 预测值)', fontsize=12)
        ax.grid(True)
    
    for j in range(num_models, len(axes)):
        axes[j].axis('off')

    plt.tight_layout()
    plt.show()

    # --- 特征重要性可视化 (仅限树模型) ---
    if feature_importances:
        print("\n--- 特征重要性可视化 ---")
        num_tree_models = len(feature_importances)
        rows_tree = (num_tree_models + cols - 1) // cols
        fig, axes = plt.subplots(rows_tree, cols, figsize=(18, 6 * rows_tree))
        axes = axes.flatten()

        for i, (name, importances) in enumerate(feature_importances.items()):
            ax = axes[i]
            sorted_indices = np.argsort(importances)[::-1]
            sorted_importances = importances[sorted_indices]
            sorted_feature_names = [feature_names[j] for j in sorted_indices]

            ax.barh(sorted_feature_names, sorted_importances, color='skyblue')
            ax.set_title(f'{name} - 特征重要性', fontsize=14)
            ax.set_xlabel('重要性', fontsize=12)
            ax.set_ylabel('特征', fontsize=12)
            ax.grid(axis='x')
        
        for j in range(num_tree_models, len(axes)):
            axes[j].axis('off')
            
        plt.tight_layout()
        plt.show()

    # --- 预测值分布图可视化 ---
    print("\n--- 预测值分布图 ---")
    fig, axes = plt.subplots(rows, cols, figsize=(18, 6 * rows))
    axes = axes.flatten()
    
    all_values = np.concatenate([y_test] + list(predictions.values()))
    bins = np.linspace(min(all_values), max(all_values), 50)

    for i, name in enumerate(models.keys()):
        ax = axes[i]
        y_pred = predictions[name]
        
        ax.hist(y_test, bins=bins, alpha=0.6, label='实际值', color='b', edgecolor='k')
        ax.hist(y_pred, bins=bins, alpha=0.6, label='预测值', color='r', edgecolor='k')
        
        ax.set_title(f'{name} - 分布图', fontsize=14)
        ax.set_xlabel('病程进展', fontsize=12)
        ax.set_ylabel('频率', fontsize=12)
        ax.legend()
        ax.grid(True)
    
    for j in range(num_models, len(axes)):
        axes[j].axis('off')

    plt.tight_layout()
    plt.show()

if __name__ == "__main__":
    perform_diabetes_analysis()