共计 2645 个字符,预计需要花费 7 分钟才能阅读完成。
1. 背景与痛点
慢性肾病(CKD)是一种全球性的健康问题,早期诊断对治疗至关重要。CKD 数据集包含了患者的临床指标(如血压、血糖、红细胞计数等)和最终的诊断结果,是机器学习在医疗领域应用的经典案例。

处理这类数据时,我们常遇到以下挑战:
- 数据不均衡:健康样本远多于患病样本,导致模型偏向多数类
- 高维度与特征相关性:临床指标间存在强相关性(如血压与肾功能指标)
- 缺失值普遍:医疗记录常有不完整字段
- 类别特征处理:如尿液蛋白检测结果(trace, +, ++ 等)需要合理编码
2. 数据探索
首先加载数据并观察其结构:
import pandas as pd
import matplotlib.pyplot as plt
# 加载数据
df = pd.read_csv('ckd.csv')
print(f'数据集形状:{df.shape}')
print('\n 前 5 行样本:')
display(df.head())
# 基本统计信息
print('\n 数值特征统计:')
display(df.describe())
# 目标变量分布
plt.figure(figsize=(6,4))
df['classification'].value_counts().plot(kind='bar')
plt.title('类别分布')
plt.show()
关键观察点:
- 数据包含 400 条记录,25 个特征(24 个特征 + 1 个目标变量)
- 目标变量
classification显示约 150 例 CKD 患者,250 例健康人 - 特征中包含数值型(如
bp血压)和分类型(如rbc红细胞计数)
3. 预处理方案
3.1 缺失值处理
医疗数据常见缺失值处理策略对比:
# 检查缺失值
missing = df.isnull().sum()
print(f'缺失值统计:\n{missing[missing > 0]}')
# 方案 1:删除缺失率高的特征
df_clean = df.drop(columns=['id']) # 本例中 id 列无意义
# 方案 2:数值特征用中位数填充
num_cols = ['age', 'bp', 'sg', 'al']
df_clean[num_cols] = df_clean[num_cols].fillna(df_clean[num_cols].median())
# 方案 3:类别特征用众数填充
cat_cols = ['rbc', 'pc']
df_clean[cat_cols] = df_clean[cat_cols].fillna(df_clean[cat_cols].mode().iloc[0])
3.2 类别特征编码
医疗类别特征的特殊性处理:
from sklearn.preprocessing import OrdinalEncoder
# 有序类别(如尿蛋白等级)ordinal_features = ['al', 'su']
ordinal_map = {'al': ['notpresent', 'trace', '1', '2', '3', '4', '5'],
'su': ['notpresent', 'trace', '1', '2', '3', '4', '5']
}
encoder = OrdinalEncoder(categories=[ordinal_map['al'], ordinal_map['su']])
df_clean[ordinal_features] = encoder.fit_transform(df_clean[ordinal_features])
# 无序类别(如红细胞形态)用 One-Hot
nominal_features = ['rbc', 'pc']
df_clean = pd.get_dummies(df_clean, columns=nominal_features)
3.3 处理数据不平衡
from imblearn.over_sampling import SMOTE
X = df_clean.drop('classification', axis=1)
y = df_clean['classification'].map({'ckd':1, 'notckd':0})
# 使用 SMOTE 过采样
smote = SMOTE(random_state=42)
X_res, y_res = smote.fit_resample(X, y)
print(f'过采样后类别分布:\n{y_res.value_counts()}')
4. 建模示例
构建完整的建模 pipeline:
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(X_res, y_res, test_size=0.2, random_state=42)
# 构建 pipeline
pipe = Pipeline([('scaler', StandardScaler()),
('clf', RandomForestClassifier(
n_estimators=100,
class_weight='balanced',
random_state=42
))
])
# 训练与评估
pipe.fit(X_train, y_train)
print(f'测试集准确率:{pipe.score(X_test, y_test):.2f}')
5. 避坑指南
处理医疗数据时需要特别注意:
- 特征泄露:确保预处理步骤(如填充缺失值)只在训练集上计算统计量
- 评估指标选择:准确率可能误导,优先考虑召回率、AUC-ROC
- 特征解释性:医疗模型需要可解释性,优先选择 SHAP 值等解释方法
- 数据标准化:不同检验指标量纲差异大,必须进行标准化
6. 延伸与练习
延伸阅读
- 《医疗机器学习实战》第 4 章 数据预处理
- SMOTE 算法原始论文
- scikit-learn 文档中的 Pipeline 使用指南
实操练习
- 尝试不同的采样策略(如 ADASYN)比较效果
- 用 GridSearchCV 优化随机森林参数
- 添加特征选择步骤,观察模型性能变化
结语
通过本文的完整流程,我们实现了从原始医疗数据到可部署模型的转化。医疗数据处理需要格外谨慎,希望这些实践经验能帮助你在类似项目中少走弯路。记住,好的数据预处理往往比复杂的模型更能提升最终效果。
正文完
