Kaggle心脏病预测实战:用Python从EDA到模型部署的完整流程(附代码避坑点)
Kaggle心脏病预测实战从数据探索到模型部署的完整Python指南当你第一次接触Kaggle上的心脏病数据集时可能会被各种医学术语和14个维度的特征搞得一头雾水。这个数据集包含了303个样本每个样本记录了从年龄、性别到心电图测量等多方面的健康指标。作为数据科学爱好者我们不仅要理解这些数据背后的医学意义更重要的是掌握如何将这些原始数据转化为可行动的预测模型。本文将带你走完从数据清洗、探索性分析到模型训练和部署的全流程特别关注那些容易被忽视但至关重要的实践细节。1. 数据理解与预处理构建高质量建模基础1.1 数据加载与初步检查开始任何数据分析项目的第一步都是彻底了解你的数据。使用pandas加载数据后我们需要进行全面的体检import pandas as pd import matplotlib.pyplot as plt import seaborn as sns # 加载数据集 heart_df pd.read_csv(heart.csv) # 基础检查清单 print(f数据集形状: {heart_df.shape}) print(\n前5行数据预览:) print(heart_df.head()) print(\n数据类型与缺失值检查:) print(heart_df.info()) print(\n描述性统计:) print(heart_df.describe())关键发现与处理建议检查到ca和thal列虽然显示为数值型但实际上是分类变量oldpeak(ST段压低值)存在负值需要确认是否为数据录入错误虽然isnull().sum()显示没有缺失值但要注意某些列中的0值可能是缺失值的占位符1.2 分类变量的特殊处理医疗数据中的分类变量往往包含重要信息但需要特殊编码# 分类变量映射字典 category_maps { sex: {0: female, 1: male}, cp: {0: asymptomatic, 1: typical, 2: atypical, 3: non-anginal}, fbs: {0: 120, 1: 120}, exang: {0: no, 1: yes}, thal: {0: missing, 1: normal, 2: fixed, 3: reversible} } # 应用映射 for col, mapping in category_maps.items(): heart_df[col] heart_df[col].map(mapping)注意在医疗数据中保留原始分类标签的语义至关重要这有助于后续结果解释和临床验证。1.3 特征工程从原始数据到更有意义的特征基于医学知识我们可以创建更有预测力的衍生特征# 创建年龄分段 heart_df[age_group] pd.cut(heart_df[age], bins[0, 40, 55, 65, 100], labels[青年, 中年, 中老年, 老年]) # 血压分类(根据医学标准) heart_df[bp_category] pd.cut(heart_df[trestbps], bins[0, 90, 120, 140, 200], labels[低血压, 正常, 高血压前期, 高血压]) # 胆固醇比值(更有医学意义) heart_df[chol_ratio] heart_df[chol] / heart_df[age]2. 探索性数据分析(EDA)发现数据背后的故事2.1 目标变量分布与类别平衡心脏病预测是一个典型的二分类问题首先检查类别分布plt.figure(figsize(10,5)) ax sns.countplot(xtarget, dataheart_df) plt.title(心脏病诊断结果分布) for p in ax.patches: ax.annotate(f{p.get_height()/len(heart_df):.1%}, (p.get_x()p.get_width()/2., p.get_height()), hacenter, vacenter, xytext(0,10), textcoordsoffset points)分析结果阳性样本(患病)165例(54.5%)阴性样本(未患病)138例(45.5%)数据基本平衡无需进行过采样或欠采样2.2 关键特征与目标变量的关系使用可视化探索各特征与心脏病诊断的关系# 设置绘图风格 plt.style.use(seaborn) # 创建特征-目标关系图 fig, axes plt.subplots(3, 3, figsize(18, 15)) # 年龄与诊断 sns.boxplot(xtarget, yage, dataheart_df, axaxes[0,0]) axes[0,0].set_title(年龄分布 vs 诊断结果) # 性别与诊断 sns.countplot(xsex, huetarget, dataheart_df, axaxes[0,1]) axes[0,1].set_title(性别分布 vs 诊断结果) # 胸痛类型与诊断 sns.countplot(xcp, huetarget, dataheart_df, axaxes[0,2]) axes[0,2].set_title(胸痛类型 vs 诊断结果) axes[0,2].tick_params(axisx, rotation15) # 最大心率与诊断 sns.violinplot(xtarget, ythalach, dataheart_df, axaxes[1,0]) axes[1,0].set_title(最大心率分布 vs 诊断结果) # ST段压低与诊断 sns.kdeplot(dataheart_df, xoldpeak, huetarget, axaxes[1,1]) axes[1,1].set_title(ST段压低分布 vs 诊断结果) # 运动诱发心绞痛与诊断 sns.countplot(xexang, huetarget, dataheart_df, axaxes[1,2]) axes[1,2].set_title(运动诱发心绞痛 vs 诊断结果) # 斜率与诊断 sns.countplot(xslope, huetarget, dataheart_df, axaxes[2,0]) axes[2,0].set_title(ST段斜率 vs 诊断结果) # 主要血管数量与诊断 sns.countplot(xca, huetarget, dataheart_df, axaxes[2,1]) axes[2,1].set_title(主要血管数量 vs 诊断结果) # 地中海贫血与诊断 sns.countplot(xthal, huetarget, dataheart_df, axaxes[2,2]) axes[2,2].set_title(地中海贫血 vs 诊断结果) axes[2,2].tick_params(axisx, rotation15) plt.tight_layout()关键发现非典型胸痛(cpatypical)的患者心脏病发病率显著更高最大心率(thalach)较低的人群患病风险更高运动诱发心绞痛(exangyes)与心脏病高度相关主要血管数量(ca)越多患病风险越高2.3 特征相关性分析使用热图分析特征间的相关性# 计算相关系数 corr heart_df.select_dtypes(include[float64,int64]).corr() # 绘制热图 plt.figure(figsize(12,10)) sns.heatmap(corr, annotTrue, fmt.2f, cmapcoolwarm, masknp.triu(np.ones_like(corr, dtypebool))) plt.title(特征相关性热图)提示对于高度相关的特征对(如age和chol_ratio)考虑保留其中一个或创建组合特征避免多重共线性问题。3. 模型构建与评估从基础到进阶3.1 数据准备与特征工程在建模前我们需要完成最后的特征处理from sklearn.preprocessing import StandardScaler, OneHotEncoder from sklearn.compose import ColumnTransformer from sklearn.pipeline import Pipeline # 定义数值型和类别型特征 numeric_features [age, trestbps, chol, thalach, oldpeak] categorical_features [sex, cp, fbs, restecg, exang, slope, ca, thal] # 创建预处理管道 preprocessor ColumnTransformer( transformers[ (num, StandardScaler(), numeric_features), (cat, OneHotEncoder(handle_unknownignore), categorical_features) ]) # 分割数据集 from sklearn.model_selection import train_test_split X heart_df.drop(target, axis1) y heart_df[target] X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42)3.2 多种模型对比实验我们将比较五种常见分类算法的表现from sklearn.linear_model import LogisticRegression from sklearn.neighbors import KNeighborsClassifier from sklearn.tree import DecisionTreeClassifier from sklearn.ensemble import RandomForestClassifier from sklearn.svm import SVC from sklearn.model_selection import cross_val_score # 定义模型 models { Logistic Regression: LogisticRegression(max_iter1000), KNN: KNeighborsClassifier(), Decision Tree: DecisionTreeClassifier(max_depth5), Random Forest: RandomForestClassifier(n_estimators100), SVM: SVC(probabilityTrue) } # 评估函数 def evaluate_models(models, X, y): results {} for name, model in models.items(): pipeline Pipeline(steps[(preprocessor, preprocessor), (classifier, model)]) scores cross_val_score(pipeline, X, y, cv5, scoringaccuracy) results[name] scores.mean() return pd.DataFrame(results.items(), columns[Model, Accuracy]) # 执行评估 model_results evaluate_models(models, X_train, y_train) print(model_results.sort_values(Accuracy, ascendingFalse))模型性能对比表模型平均准确率训练时间可解释性随机森林0.85中等高逻辑回归0.83快高SVM0.82慢低决策树0.81快中KNN0.79快低3.3 随机森林模型优化基于初步结果我们重点优化随机森林模型from sklearn.model_selection import GridSearchCV # 定义参数网格 param_grid { classifier__n_estimators: [50, 100, 200], classifier__max_depth: [None, 5, 10], classifier__min_samples_split: [2, 5, 10], classifier__min_samples_leaf: [1, 2, 4] } # 创建完整管道 pipeline Pipeline(steps[(preprocessor, preprocessor), (classifier, RandomForestClassifier(random_state42))]) # 网格搜索 grid_search GridSearchCV(pipeline, param_grid, cv5, scoringaccuracy, n_jobs-1) grid_search.fit(X_train, y_train) # 最佳参数 print(f最佳参数: {grid_search.best_params_}) print(f最佳分数: {grid_search.best_score_:.4f})特征重要性分析# 获取最佳模型 best_model grid_search.best_estimator_ # 提取特征重要性 importances best_model.named_steps[classifier].feature_importances_ # 获取特征名称 feature_names (numeric_features list(best_model.named_steps[preprocessor] .named_transformers_[cat] .get_feature_names_out(categorical_features))) # 创建重要性DataFrame importance_df pd.DataFrame({Feature: feature_names, Importance: importances}) importance_df importance_df.sort_values(Importance, ascendingFalse) # 可视化 plt.figure(figsize(12,8)) sns.barplot(xImportance, yFeature, dataimportance_df.head(15)) plt.title(Top 15 重要特征) plt.tight_layout()4. 模型部署从Jupyter Notebook到生产环境4.1 模型保存与加载训练好的模型需要持久化保存import joblib from datetime import datetime # 保存最佳模型 model_filename fheart_disease_model_{datetime.now().strftime(%Y%m%d)}.pkl joblib.dump(best_model, model_filename) # 加载模型示例 loaded_model joblib.load(model_filename)4.2 创建预测API使用Flask创建简单的预测接口from flask import Flask, request, jsonify import pandas as pd app Flask(__name__) # 加载模型 model joblib.load(model_filename) app.route(/predict, methods[POST]) def predict(): try: # 获取输入数据 input_data request.json # 转换为DataFrame input_df pd.DataFrame([input_data]) # 预测 prediction model.predict(input_df) probability model.predict_proba(input_df) # 返回结果 return jsonify({ prediction: int(prediction[0]), probability: float(probability[0][1]), status: success }) except Exception as e: return jsonify({status: error, message: str(e)}) if __name__ __main__: app.run(host0.0.0.0, port5000)4.3 API测试与使用使用curl测试API端点curl -X POST http://localhost:5000/predict \ -H Content-Type: application/json \ -d { age: 58, sex: male, cp: atypical, trestbps: 140, chol: 289, fbs: 120, restecg: normal, thalach: 172, exang: no, oldpeak: 0.0, slope: upsloping, ca: 0, thal: normal }预期响应{ prediction: 1, probability: 0.92, status: success }4.4 部署优化建议对于生产环境考虑以下优化措施输入验证确保所有必填字段存在且值在合理范围内性能监控记录预测延迟和系统资源使用情况模型版本控制支持多模型版本并存和A/B测试自动缩放使用Kubernetes或类似技术根据负载自动调整资源安全防护实现身份验证和速率限制5. 项目总结与进阶方向在完成这个心脏病预测项目后有几个关键经验值得分享数据质量决定上限医疗数据中的异常值和编码问题会显著影响模型性能花在数据清洗上的时间通常比建模更多特征工程的艺术基于医学知识创建的特征(如胆固醇年龄比)往往比原始特征更有预测力模型可解释性的重要性在医疗领域能够解释为什么做出特定预测通常比绝对准确率更重要进阶探索方向尝试深度学习模型(如神经网络)并比较性能集成多个模型创建更强大的预测系统开发前端界面使临床医生能更方便地使用预测工具添加模型解释功能(如SHAP值)帮助理解预测依据