基于随机森林、XGBoost 和 LightGBM 的心脏病症状预测数据分析系统是一个典型的机器学习分类项目,旨在通过患者的临床特征(如年龄、血压、胆固醇水平等)预测其是否患有心脏病。以下是一个完整的毕业设计解决方案,包含:**数据集介绍、数据预处理方案、模型构建与评估流程、完整 Python 源代码及详细解析**。
---
### ✅ 一、数据集介绍
我们使用公开的心脏病数据集:**Heart Disease UCI Dataset**,由美国加州大学欧文分校(UCI)提供。
- **数据来源**:[https://archive.ics.uci.edu/ml/datasets/Heart+Disease](https://archive.ics.uci.edu/ml/datasets/Heart+Disease)
- **样本数量**:303 条患者记录
- **特征数量**:14 个(含目标变量)
- **目标变量**:`target` —— 是否患有心脏病(0 = 无,1 = 有)
#### 特征说明:
| 字段名 | 含义 |
|--------|------|
| age | 年龄 |
| sex | 性别 (0 = 女, 1 = 男) |
| cp | 胸痛类型 (0~3) |
| trestbps | 静息血压 (mm Hg) |
| chol | 血清胆固醇 (mg/dl) |
| fbs | 空腹血糖 > 120 mg/dL (0 = 否, 1 = 是) |
| restecg | 静息心电图结果 (0~2) |
| thalach | 最大心率 |
| exang | 运动诱发心绞痛 (0 = 否, 1 = 是) |
| oldpeak | 相对于静息状态的 ST 抑制程度 |
| slope | ST 段斜率 (0~2) |
| ca | 主要血管着色数 (0~3) |
| thal | 地中海贫血类型 (3 = 正常, 6 = 固定缺陷, 7 = 可逆缺陷) |
| target | 是否患病 (0 或 1) |
> 📌 注意:原始数据可能分散在多个文件中,但通常合并为 `heart.csv` 使用。
---
### ✅ 二、整体数据设计方案
#### 1. 数据获取与加载
- 下载 `heart.csv` 文件并读取。
- 使用 pandas 加载数据。
#### 2. 数据探索性分析(EDA)
- 查看缺失值、异常值。
- 绘制分布图、相关性热力图。
- 分析类别平衡情况。
#### 3. 数据预处理
- 缺失值填充或删除。
- 对离散变量进行独热编码(One-Hot Encoding),如 `cp`, `thal`, `slope`。
- 标准化连续变量(可选)。
- 划分训练集和测试集(8:2)。
#### 4. 模型选择与训练
- 使用三种集成算法:
- Random Forest(随机森林)
- XGBoost
- LightGBM
- 使用交叉验证调优超参数(GridSearchCV / Optuna 可选)。
#### 5. 模型评估
- 准确率(Accuracy)、精确率(Precision)、召回率(Recall)、F1-score、AUC-ROC 曲线。
- 混淆矩阵可视化。
#### 6. 系统输出
- 输出最佳模型用于预测新数据。
- 提供命令行或简单 Web 接口(Flask 实现)输入特征后返回预测结果。
---
### ✅ 三、完整源代码(Python)
```python
# -*- coding: utf-8 -*-
"""
心脏病预测系统 - 基于随机森林、XGBoost、LightGBM
"""
import warnings
warnings.filterwarnings('ignore')
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.ensemble import RandomForestClassifier
from xgboost import XGBClassifier
from lightgbm import LGBMClassifier
from sklearn.metrics import classification_report, confusion_matrix, roc_auc_score, roc_curve
import joblib
# -----------------------------
# 1. 加载数据
# -----------------------------
df = pd.read_csv('heart.csv') # 确保文件在同一目录下
print("数据形状:", df.shape)
print("\n前五行数据:")
print(df.head())
# -----------------------------
# 2. EDA 探索性数据分析
# -----------------------------
print("\n数据信息:")
print(df.info())
print("\n缺失值统计:")
print(df.isnull().sum())
# 查看目标变量分布
sns.countplot(x='target', data=df)
plt.title('Heart Disease Distribution')
plt.show()
# 相关性热力图
plt.figure(figsize=(12, 8))
sns.heatmap(df.corr(), annot=True, fmt='.2f', cmap='coolwarm')
plt.title('Feature Correlation Heatmap')
plt.show()
# -----------------------------
# 3. 数据预处理
# -----------------------------
# 区分离散特征和连续特征
categorical_features = ['sex', 'cp', 'fbs', 'restecg', 'exang', 'slope', 'ca', 'thal']
numerical_features = ['age', 'trestbps', 'chol', 'thalach', 'oldpeak']
# 构建预处理器
preprocessor = ColumnTransformer(
transformers=[
('num', StandardScaler(), numerical_features),
('cat', OneHotEncoder(drop='first'), categorical_features)
])
# 分离特征与标签
X = df.drop('target', axis=1)
y = df['target']
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)
# -----------------------------
# 4. 构建模型管道
# -----------------------------
models = {
"Random Forest": RandomForestClassifier(n_estimators=100, random_state=42),
"XGBoost": XGBClassifier(use_label_encoder=False, eval_metric='logloss', random_state=42),
"LightGBM": LGBMClassifier(random_state=42)
}
# 存储结果
results = {}
for name, model in models.items():
print(f"\n{'='*50}")
print(f"正在训练 {name}...")
# 创建流水线
pipeline = Pipeline(steps=[('preprocessor', preprocessor),
('classifier', model)])
# 训练
pipeline.fit(X_train, y_train)
# 预测
y_pred = pipeline.predict(X_test)
y_pred_proba = pipeline.predict_proba(X_test)[:, 1]
# 评估指标
accuracy = pipeline.score(X_test, y_test)
auc = roc_auc_score(y_test, y_pred_proba)
results[name] = {
'model': pipeline,
'accuracy': accuracy,
'auc': auc,
'y_pred': y_pred,
'y_pred_proba': y_pred_proba
}
print(f"{name} 准确率: {accuracy:.4f}")
print(f"{name} AUC: {auc:.4f}")
print(f"分类报告:\n{classification_report(y_test, y_pred)}")
# -----------------------------
# 5. 混淆矩阵对比
# -----------------------------
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
fig.suptitle("Confusion Matrices")
for idx, (name, res) in enumerate(results.items()):
cm = confusion_matrix(y_test, res['y_pred'])
sns.heatmap(cm, annot=True, fmt='d', ax=axes[idx], cmap='Blues')
axes[idx].set_title(f'{name}')
axes[idx].set_xlabel('Predicted')
axes[idx].set_ylabel('Actual')
plt.tight_layout()
plt.show()
# -----------------------------
# 6. ROC 曲线对比
# -----------------------------
plt.figure(figsize=(8, 6))
for name, res in results.items():
fpr, tpr, _ = roc_curve(y_test, res['y_pred_proba'])
plt.plot(fpr, tpr, label=f"{name} (AUC = {res['auc']:.3f})")
plt.plot([0, 1], [0, 1], 'k--', lw=2, label='Random Guess')
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC Curve Comparison')
plt.legend()
plt.grid(True)
plt.show()
# -----------------------------
# 7. 保存最佳模型
# -----------------------------
best_model_name = max(results.keys(), key=lambda x: results[x]['auc'])
best_pipeline = results[best_model_name]['model']
joblib.dump(best_pipeline, 'best_heart_disease_model.pkl')
print(f"\n✅ 最佳模型 '{best_model_name}' 已保存为 'best_heart_disease_model.pkl'")
# -----------------------------
# 8. 示例:对新患者进行预测
# -----------------------------
def predict_patient(model, patient_data):
"""
输入:patient_data 为 DataFrame 形式的一条记录
"""
pred = model.predict(patient_data)[0]
prob = model.predict_proba(patient_data)[0][1]
print(f"预测结果: {'患有心脏病' if pred == 1 else '未患心脏病'}")
print(f"患病概率: {prob:.2%}")
return pred, prob
# 示例病人(需与原数据格式一致)
sample_patient = pd.DataFrame([{
'age': 60,
'sex': 1,
'cp': 2,
'trestbps': 145,
'chol': 233,
'fbs': 1,
'restecg': 0,
'thalach': 150,
'exang': 0,
'oldpeak': 2.3,
'slope': 1,
'ca': 0,
'thal': 2 # 注意:原始 thal 中 3=正常, 6=固定, 7=可逆 → 编码后注意映射
}], columns=X.columns)
print("\n📌 对新患者进行预测:")
predict_patient(best_pipeline, sample_patient)
```
---
### ✅ 四、代码解析
| 模块 | 功能说明 |
|------|----------|
| `pandas/numpy` | 数据加载与基本操作 |
| `matplotlib/seaborn` | 可视化:热力图、混淆矩阵、ROC曲线 |
| `sklearn.model_selection` | 划分训练/测试集 |
| `ColumnTransformer + Pipeline` | 自动化预处理流程(标准化+独热编码) |
| `RandomForest/XGBoost/LightGBM` | 三大主流集成学习器 |
| `classification_report/confusion_matrix` | 多维度评估模型性能 |
| `joblib` | 模型持久化存储 |
| `predict_patient()` 函数 | 封装预测接口,便于部署 |
> 💡 **关键点**:
> - 使用 `Pipeline` 可避免数据泄露,并提升代码可维护性。
> - `OneHotEncoder(drop='first')` 防止多重共线性。
> - `stratify=y` 确保训练/测试集中正负样本比例一致。
> - 所有模型统一用相同预处理流程,公平比较。
---
### ✅ 五、扩展建议(可用于论文加分项)
1. **超参数调优**
- 使用 `GridSearchCV` 或 `Optuna` 自动搜索最优参数。
2. **SHAP 解释性分析**
- 展示每个特征对预测的影响(适合答辩展示)。
3. **Flask Web 系统**
- 开发一个简单的网页表单让用户输入体检数据并返回预测结果。
4. **模型融合(Stacking)**
- 将三个模型组合成一个更强的元模型。
5. **移动端 APP 或微信小程序**
- 结合 Flask API + 小程序前端实现交互式应用。
---
### ✅ 六、所需环境安装命令
```bash
pip install pandas numpy matplotlib seaborn scikit-learn xgboost lightgbm joblib flask
```
---
### ✅ 七、如何运行项目?
1. 下载 `heart.csv` 放入项目根目录。
2. 运行上述 `.py` 脚本。
3. 查看控制台输出和图表。
4. 使用生成的 `.pkl` 文件做后续预测。
---
### ✅ 八、常见问题解决
| 问题 | 解决方法 |
|------|---------|
| `ValueError: could not convert string to float` | 确保所有列是数值型,检查数据清洗 |
| `KeyError: 'thal'` | 检查 CSV 是否包含所有字段 |
| XGBoost 警告 `use_label_encoder` | 已添加 `eval_metric='logloss'` 忽略警告 |
| OneHotEncoder 报错维度不匹配 | 确保预测输入字段顺序与训练一致 |
---
### ✅ 九、参考文献
1. UCI Machine Learning Repository: Heart Disease Dataset
https://archive.ics.uci.edu/ml/datasets/Heart+Disease
2. Scikit-learn Documentation
https://scikit-learn.org
3. Chen, T., & Guestrin, C. (2016). XGBoost: A Scalable Tree Boosting System. *KDD*.
4. Ke, G., et al. (2017). LightGBM: A Highly Efficient Gradient Boosting Decision Tree. *NeurIPS*.
---
###