从UCI心脏病数据集到临床预测模型:数据预处理与特征工程实战
2026/6/30 14:17:47 网站建设 项目流程

1. 认识UCI心脏病数据集

第一次接触UCI心脏病数据集时,我被它丰富的临床属性吸引了。这个数据集包含了来自四个不同医疗机构的患者数据,但最常用的是克利夫兰诊所的数据。数据集有14个关键特征,从基本的人口统计信息到专业的医学指标,覆盖了诊断心脏病所需的方方面面。

我特别喜欢这个数据集的一点是,它非常贴近真实临床场景。比如胸痛类型(cp)这个特征,就包含了四种不同的心绞痛分类,这在临床上非常重要。还有thal这个指标,它反映的是一种叫做地中海贫血的血液疾病,这个特征在大多数公开数据集中都很难见到。

不过这个数据集也有些"坑"需要注意。最大的问题是缺失值,特别是ca(主要血管数目)和thal(地中海贫血)这两个特征。我在第一次使用时没注意这个问题,直接建模结果惨不忍睹。后来才发现数据集里有6条记录存在缺失值,必须得先处理这个问题。

2. 数据清洗实战

2.1 处理缺失值

处理缺失值前,我习惯先用pandas做个快速检查:

import pandas as pd data = pd.read_csv('processed.cleveland.csv') print(data.isnull().sum())

结果显示ca列有4个缺失值,thal列有2个缺失值。对于医学数据,我一般不会用均值填充,因为每个患者的状况都很独特。我的做法是直接删除这些记录:

data.dropna(inplace=True)

删除后还剩297条记录。虽然损失了一些数据,但保证了数据的可靠性。如果你实在不想删除,可以考虑用众数填充ca列,因为它的取值是离散的0-3;thal列则可以用"正常"作为默认值填充。

2.2 统一编码格式

原始数据中,thal和target列的编码不太一致。比如thal在原始数据中用3、6、7表示不同状态,而在处理后的版本中用0、1、2表示。为了让模型更好理解,我建议统一编码:

# 统一thal编码 data['thal'] = data['thal'].replace({3:0, 6:1, 7:2}) # 将target转为二分类 data['target'] = data['target'].apply(lambda x: 1 if x > 0 else 0)

这样处理后,所有分类特征都有了统一的编码规范,模型训练时会更加稳定。

3. 特征工程技巧

3.1 特征理解与转换

这个数据集有几个特征需要特别注意:

  1. 年龄(age):我习惯将其分箱处理,因为心脏病风险与年龄不是简单的线性关系。可以这样操作:
data['age_group'] = pd.cut(data['age'], bins=[0,40,50,60,70,100], labels=['<40','40-50','50-60','60-70','>70'])
  1. 血压(trestbps)胆固醇(chol):这些连续值特征我通常会做标准化处理:
from sklearn.preprocessing import StandardScaler scaler = StandardScaler() data[['trestbps','chol']] = scaler.fit_transform(data[['trestbps','chol']])
  1. thalach(最大心率):这个特征很有意思,我发现在不同年龄段的正常范围不同,所以创建了一个新的特征"心率年龄比":
data['hr_age_ratio'] = data['thalach'] / data['age']

3.2 特征相关性分析

在建模前,我必做的一件事是分析特征相关性。用seaborn画热力图是个好方法:

import seaborn as sns import matplotlib.pyplot as plt plt.figure(figsize=(12,10)) sns.heatmap(data.corr(), annot=True, cmap='coolwarm') plt.show()

从我的分析结果看,exang(运动引起的心绞痛)和oldpeak(ST段压低)与目标变量的相关性最高,而fbs(空腹血糖)的相关性很低。这提示我们在特征选择时可能需要有所侧重。

4. 构建预测模型

4.1 数据准备

在建模前,我们需要做最后的准备工作:

from sklearn.model_selection import train_test_split # 选择特征 features = ['age', 'sex', 'cp', 'trestbps', 'chol', 'fbs', 'restecg', 'thalach', 'exang', 'oldpeak', 'slope', 'ca', 'thal'] X = data[features] y = data['target'] # 划分训练测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

4.2 模型训练与评估

我比较喜欢先用随机森林做个baseline,因为它对特征工程的要求相对较低:

from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import accuracy_score, f1_score rf = RandomForestClassifier(n_estimators=100, random_state=42) rf.fit(X_train, y_train) y_pred = rf.predict(X_test) print(f"准确率: {accuracy_score(y_test, y_pred):.2f}") print(f"F1分数: {f1_score(y_test, y_pred):.2f}")

在我的测试中,这个baseline模型能达到约85%的准确率。如果想进一步提升性能,可以尝试以下方法:

  1. 调整类别权重,因为数据集中阳性和阴性样本的比例可能不均衡
  2. 使用网格搜索优化超参数
  3. 尝试其他算法如XGBoost或逻辑回归

5. 实际应用中的注意事项

在临床环境中应用这个模型时,有几个关键点需要注意:

首先,不同医疗机构的数据收集标准可能不同。比如血压测量方法、胆固醇检测方式等,这些都会影响模型的泛化能力。我建议在实际部署前,用本地数据对模型进行微调。

其次,要特别注意特征的解释性。医生们更愿意相信那些他们能理解的预测结果。因此,使用SHAP或LIME等可解释性工具非常重要:

import shap explainer = shap.TreeExplainer(rf) shap_values = explainer.shap_values(X_test) shap.summary_plot(shap_values[1], X_test, feature_names=features)

最后,要定期更新模型。医学知识和技术在不断进步,模型的预测标准也应该与时俱进。我通常每半年就会用新数据重新训练一次模型。

这个项目最让我有成就感的是,经过适当的数据预处理和特征工程后,即使是相对简单的模型也能达到不错的预测效果。这再次验证了在机器学习项目中,数据和特征的质量往往比算法选择更重要。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询