用Python+机器学习解锁健康数据:慢性病预测实战指南
当一份包含数百个变量的居民健康调查数据摆在面前时,大多数人的第一反应可能是无从下手。这些密密麻麻的数字背后,隐藏着哪些健康密码?本文将带你用Python和机器学习技术,像侦探一样抽丝剥茧,从原始数据到可行动的洞察。
1. 数据准备与初步探索
拿到健康调查数据的第一步不是急着建模,而是先了解数据的"脾气"。假设我们手上有类似深圳杯数学建模A题中的数据集,包含居民的年龄、饮食习惯、运动频率、慢性病情况等信息。
import pandas as pd import matplotlib.pyplot as plt # 加载数据 health_data = pd.read_csv('居民健康调查.csv') print(f"数据集形状: {health_data.shape}") print("\n前5行数据预览:") print(health_data.head())关键检查点:
- 缺失值比例:用
health_data.isnull().mean()查看每列缺失情况 - 数据类型:
health_data.dtypes确认数值型和类别型变量 - 异常值检测:绘制箱线图快速发现离群点
提示:健康数据中常见问题包括血压值为负数、年龄超过合理范围等,这些都需要在预处理阶段解决
慢性病分布的可视化示例:
chronic_disease_counts = health_data['慢性病类型'].value_counts() plt.figure(figsize=(10,6)) chronic_disease_counts.plot(kind='bar') plt.title('不同慢性病患病分布') plt.xlabel('疾病类型') plt.ylabel('人数') plt.xticks(rotation=45) plt.show()2. 数据清洗与特征工程实战
原始健康数据往往像未经雕琢的玉石,需要精心打磨才能展现价值。这个阶段的目标是将杂乱的数据转化为机器学习模型能够理解的格式。
常见处理步骤:
缺失值处理:
- 连续变量:均值/中位数填充
- 分类变量:单独设为"未知"类别
- 大量缺失的特征:考虑直接删除
特征转换:
- 分类变量编码:
pd.get_dummies()或LabelEncoder - 年龄分段:将连续年龄转化为青年、中年、老年等区间
- 饮食习惯评分:根据膳食指南量化评分
- 分类变量编码:
from sklearn.preprocessing import MinMaxScaler # 示例:饮食习惯量化 def diet_score(row): score = 0 if row['蔬菜摄入'] >= 300: score += 1 if row['盐摄入'] < 5: score += 1 # 其他评分规则... return score health_data['饮食评分'] = health_data.apply(diet_score, axis=1) # 数值特征标准化 scaler = MinMaxScaler() num_features = ['年龄', 'BMI', '运动小时数'] health_data[num_features] = scaler.fit_transform(health_data[num_features])特征相关性分析:
corr_matrix = health_data.corr() plt.figure(figsize=(12,8)) sns.heatmap(corr_matrix, annot=True, cmap='coolwarm') plt.title('特征相关性热力图') plt.show()3. 机器学习模型构建与比较
有了清洗好的数据,就可以开始构建预测模型了。慢性病预测通常属于分类问题,我们可以尝试几种不同算法并比较效果。
模型选择矩阵:
| 模型 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 逻辑回归 | 线性关系, 需要解释性 | 训练快, 可解释强 | 只能处理线性边界 |
| 随机森林 | 复杂关系, 特征多 | 抗过拟合, 特征重要性 | 可能内存消耗大 |
| XGBoost | 不平衡数据, 需要高精度 | 表现优异, 内置特征选择 | 调参复杂 |
| 神经网络 | 大数据量, 复杂模式 | 自动特征工程, 灵活 | 需要大量数据, 黑箱 |
示例代码:随机森林实现
from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report # 准备数据 X = health_data.drop(['慢性病类型'], axis=1) y = health_data['慢性病类型'] X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42) # 训练模型 rf_model = RandomForestClassifier(n_estimators=100, max_depth=5, random_state=42) rf_model.fit(X_train, y_train) # 评估 y_pred = rf_model.predict(X_test) print(classification_report(y_test, y_pred)) # 特征重要性 importances = rf_model.feature_importances_ features = X.columns pd.DataFrame({'feature':features, 'importance':importances}).sort_values('importance', ascending=False)注意:健康数据通常不平衡(健康人多于患者),可以使用class_weight参数或过采样技术解决
4. 模型优化与结果解释
得到初步模型后,我们需要确保它不仅在测试集上表现良好,还要能提供有意义的健康洞见。
超参数调优示例:
from sklearn.model_selection import GridSearchCV param_grid = { 'n_estimators': [50, 100, 200], 'max_depth': [3, 5, 7], 'min_samples_split': [2, 5, 10] } grid_search = GridSearchCV(RandomForestClassifier(random_state=42), param_grid, cv=5, scoring='f1_weighted') grid_search.fit(X_train, y_train) print(f"最佳参数: {grid_search.best_params_}") print(f"最佳分数: {grid_search.best_score_:.3f}")模型解释技术:
- SHAP值分析:
import shap explainer = shap.TreeExplainer(rf_model) shap_values = explainer.shap_values(X_test) # 可视化单个预测解释 shap.initjs() shap.force_plot(explainer.expected_value[1], shap_values[1][0,:], X_test.iloc[0,:])- 部分依赖图:
from sklearn.inspection import PartialDependenceDisplay features = ['年龄', 'BMI', '饮食评分'] PartialDependenceDisplay.from_estimator(rf_model, X_train, features) plt.show()5. 部署与应用:从预测到预防
模型最终价值在于指导实际行动。我们可以基于模型结果开发个性化健康建议系统。
健康建议生成逻辑:
def generate_health_advice(row, model): # 预测患病风险 risk = model.predict_proba(row)[0][1] advice = [] if risk > 0.7: advice.append("您的慢性病风险较高,建议:") if row['运动小时数'] < 0.3: advice.append("- 增加每周运动时间至150分钟以上") if row['饮食评分'] < 4: advice.append("- 改善饮食结构,增加蔬菜水果摄入") # 其他建议... else: advice.append("您当前的生活方式较为健康,继续保持!") return '\n'.join(advice) # 示例应用 sample_person = X_test.iloc[0:1] print(generate_health_advice(sample_person, rf_model))结果可视化仪表盘:
import plotly.express as px # 风险分布图 risk_scores = rf_model.predict_proba(X_test)[:,1] fig = px.histogram(x=risk_scores, nbins=20, labels={'x':'患病风险得分'}, title='测试集人群风险分布') fig.show() # 特征影响雷达图 top_features = pd.DataFrame({'feature':features, 'importance':importances} ).nlargest(5, 'importance') fig = px.line_polar(top_features, r='importance', theta='feature', line_close=True) fig.update_traces(fill='toself') fig.show()在实际项目中,我发现最耗时的往往不是建模本身,而是前期的数据理解和清洗阶段。一份处理得当的健康数据,即使使用简单模型也能获得不错的效果。而特征工程的质量直接决定了模型性能的上限。