Day 14: 医疗数据分析实战
学习目标
完成今天的学习后,你将能够:
- 理解医疗数据特点
- 分析患者数据
- 进行疾病预测
- 分析治疗效果
技术原理
医疗数据特点
数据类型
- 结构化数据:检验结果、生命体征
- 非结构化数据:病历、影像
- 时间序列数据:连续监测数据
- 基因组数据:基因表达、突变
数据挑战
- 隐私保护:患者隐私
- 数据质量:缺失值、异常值
- 数据不平衡:疾病样本少
- 多模态数据:不同类型数据融合
患者数据分析
分析维度
- 人口统计:年龄、性别、地域
- 疾病特征:诊断、症状、病程
- 治疗方案:药物、手术、康复
- 预后评估:恢复情况、复发风险
疾病预测
预测方法
- 统计模型:逻辑回归、生存分析
- 机器学习:随机森林、XGBoost
- 深度学习:神经网络、LSTM
- 集成方法:模型融合
治疗效果分析
分析方法
- 随机对照试验:RCT
- 观察性研究:队列研究、病例对照
- 生存分析:Kaplan-Meier、Cox回归
- 成本效益分析:经济学评价
案例:糖尿病数据分析
假设我们有一个糖尿病数据集,需要:
- 分析患者特征
- 预测糖尿病风险
- 分析治疗效果
- 识别关键风险因素
应用场景
- 疾病预测:早期筛查、风险评估
- 精准医疗:个性化治疗方案
- 药物研发:临床试验、药效评估
- 医疗管理:资源配置、质量控制
代码案例
患者数据分析
python
# 患者数据分析示例
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import load_diabetes
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 1. 加载糖尿病数据集
diabetes = load_diabetes()
X = pd.DataFrame(diabetes.data, columns=diabetes.feature_names)
y = pd.Series(diabetes.target, name='目标值')
# 合并数据
df = pd.concat([X, y], axis=1)
print("糖尿病数据集:")
print(df.head())
print(f"\n数据形状: {df.shape}")
print(f"\n特征名称: {list(diabetes.feature_names)}")
# 2. 基本统计分析
print("\n基本统计分析:")
print("=" * 50)
print("\n描述性统计:")
print(df.describe())
# 3. 特征分布分析
fig, axes = plt.subplots(3, 4, figsize=(16, 12))
axes = axes.flatten()
for i, col in enumerate(diabetes.feature_names):
axes[i].hist(df[col], bins=30, color='skyblue', edgecolor='black', alpha=0.7)
axes[i].set_title(f'{col}分布', fontsize=12, fontweight='bold')
axes[i].set_xlabel(col)
axes[i].set_ylabel('频数')
# 隐藏多余的子图
for j in range(len(diabetes.feature_names), len(axes)):
axes[j].set_visible(False)
plt.suptitle('特征分布', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()
# 4. 相关性分析
print("\n相关性分析:")
print("=" * 50)
correlation_matrix = df.corr()
print("\n相关性矩阵:")
print(correlation_matrix)
# 可视化相关性矩阵
plt.figure(figsize=(12, 10))
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', center=0, fmt='.2f')
plt.title('特征相关性热力图', fontsize=16, fontweight='bold')
plt.show()
# 5. 目标值分析
print("\n目标值分析:")
print("=" * 50)
print(f"\n目标值统计:")
print(f"均值: {df['目标值'].mean():.2f}")
print(f"中位数: {df['目标值'].median():.2f}")
print(f"标准差: {df['目标值'].std():.2f}")
print(f"最小值: {df['目标值'].min():.2f}")
print(f"最大值: {df['目标值'].max():.2f}")
# 目标值分布
plt.figure(figsize=(10, 6))
plt.hist(df['目标值'], bins=30, color='skyblue', edgecolor='black', alpha=0.7)
plt.axvline(df['目标值'].mean(), color='red', linestyle='--', linewidth=2, label=f'均值: {df["目标值"].mean():.2f}')
plt.axvline(df['目标值'].median(), color='green', linestyle='--', linewidth=2, label=f'中位数: {df["目标值"].median():.2f}')
plt.title('目标值分布', fontsize=16, fontweight='bold')
plt.xlabel('目标值')
plt.ylabel('频数')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
# 6. 特征与目标值关系
fig, axes = plt.subplots(2, 5, figsize=(20, 8))
axes = axes.flatten()
for i, col in enumerate(diabetes.feature_names):
axes[i].scatter(df[col], df['目标值'], alpha=0.5, color='skyblue', edgecolors='black')
axes[i].set_title(f'{col} vs 目标值', fontsize=12, fontweight='bold')
axes[i].set_xlabel(col)
axes[i].set_ylabel('目标值')
# 添加趋势线
z = np.polyfit(df[col], df['目标值'], 1)
p = np.poly1d(z)
axes[i].plot(df[col], p(df[col]), "r--", linewidth=2)
plt.suptitle('特征与目标值关系', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()疾病预测
python
# 疾病预测示例
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression, Ridge, Lasso
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 1. 加载数据
diabetes = load_diabetes()
X = pd.DataFrame(diabetes.data, columns=diabetes.feature_names)
y = pd.Series(diabetes.target, name='目标值')
# 2. 数据预处理
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 标准化
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
print(f"训练集大小: {X_train.shape[0]}")
print(f"测试集大小: {X_test.shape[0]}")
# 3. 训练多个模型
print("\n模型训练:")
print("=" * 50)
models = {
'线性回归': LinearRegression(),
'岭回归': Ridge(alpha=1.0),
'Lasso回归': Lasso(alpha=1.0),
'随机森林': RandomForestRegressor(n_estimators=100, random_state=42),
'梯度提升': GradientBoostingRegressor(n_estimators=100, random_state=42)
}
results = {}
for name, model in models.items():
# 训练模型
model.fit(X_train_scaled, y_train)
# 预测
y_pred = model.predict(X_test_scaled)
# 计算指标
mse = mean_squared_error(y_test, y_pred)
rmse = np.sqrt(mse)
mae = mean_absolute_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)
results[name] = {
'MSE': mse,
'RMSE': rmse,
'MAE': mae,
'R²': r2
}
print(f"\n{name}:")
print(f" MSE: {mse:.4f}")
print(f" RMSE: {rmse:.4f}")
print(f" MAE: {mae:.4f}")
print(f" R²: {r2:.4f}")
# 4. 模型比较
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
metrics = ['MSE', 'RMSE', 'MAE', 'R²']
for i, metric in enumerate(metrics):
row = i // 2
col = i % 2
values = [results[name][metric] for name in results.keys()]
bars = axes[row, col].bar(results.keys(), values, color='skyblue', edgecolor='black')
axes[row, col].set_title(f'{metric}比较', fontsize=14, fontweight='bold')
axes[row, col].set_ylabel(metric)
axes[row, col].tick_params(axis='x', rotation=45)
# 添加数值标签
for bar in bars:
height = bar.get_height()
axes[row, col].text(bar.get_x() + bar.get_width()/2., height,
f'{height:.4f}',
ha='center', va='bottom', fontsize=9)
plt.suptitle('模型性能比较', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()
# 5. 最佳模型分析
best_model_name = max(results.keys(), key=lambda x: results[x]['R²'])
best_model = models[best_model_name]
print(f"\n最佳模型: {best_model_name}")
print("=" * 50)
y_pred = best_model.predict(X_test_scaled)
# 预测值vs实际值
plt.figure(figsize=(10, 8))
plt.scatter(y_test, y_pred, alpha=0.6, color='skyblue', edgecolors='black')
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--', linewidth=2)
plt.title('预测值 vs 实际值', fontsize=16, fontweight='bold')
plt.xlabel('实际值')
plt.ylabel('预测值')
plt.grid(True, alpha=0.3)
plt.show()
# 残差分析
residuals = y_test - y_pred
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
# 残差分布
axes[0].hist(residuals, bins=30, color='skyblue', edgecolor='black', alpha=0.7)
axes[0].axvline(x=0, color='red', linestyle='--', linewidth=2)
axes[0].set_title('残差分布', fontsize=14, fontweight='bold')
axes[0].set_xlabel('残差')
axes[0].set_ylabel('频数')
axes[0].grid(True, alpha=0.3)
# 残差vs预测值
axes[1].scatter(y_pred, residuals, alpha=0.6, color='skyblue', edgecolors='black')
axes[1].axhline(y=0, color='red', linestyle='--', linewidth=2)
axes[1].set_title('残差 vs 预测值', fontsize=14, fontweight='bold')
axes[1].set_xlabel('预测值')
axes[1].set_ylabel('残差')
axes[1].grid(True, alpha=0.3)
plt.suptitle('残差分析', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()
# 6. 特征重要性
if hasattr(best_model, 'feature_importances_'):
feature_importance = best_model.feature_importances_
# 创建特征重要性DataFrame
importance_df = pd.DataFrame({
'特征': diabetes.feature_names,
'重要性': feature_importance
}).sort_values('重要性', ascending=False)
print("\n特征重要性:")
print(importance_df)
# 可视化特征重要性
plt.figure(figsize=(10, 6))
plt.barh(importance_df['特征'], importance_df['重要性'], color='skyblue', edgecolor='black')
plt.title('特征重要性', fontsize=16, fontweight='bold')
plt.xlabel('重要性')
plt.ylabel('特征')
plt.grid(True, alpha=0.3, axis='x')
plt.tight_layout()
plt.show()治疗效果分析
python
# 治疗效果分析示例
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 1. 创建模拟临床试验数据
np.random.seed(42)
n_patients = 200
# 生成患者数据
patient_data = {
'患者ID': range(1, n_patients + 1),
'年龄': np.random.randint(30, 80, n_patients),
'性别': np.random.choice(['男', '女'], n_patients),
'治疗组': np.random.choice(['实验组', '对照组'], n_patients),
'基线指标': np.random.normal(100, 15, n_patients),
}
df = pd.DataFrame(patient_data)
# 模拟治疗效果
# 实验组效果更好
df['治疗后指标'] = df.apply(lambda row:
row['基线指标'] - np.random.normal(20, 5) if row['治疗组'] == '实验组'
else row['基线指标'] - np.random.normal(10, 5), axis=1)
df['改善程度'] = df['基线指标'] - df['治疗后指标']
df['改善率'] = (df['改善程度'] / df['基线指标']) * 100
print("临床试验数据:")
print(df.head())
print(f"\n数据形状: {df.shape}")
# 2. 治疗组对比分析
print("\n治疗组对比分析:")
print("=" * 50)
# 按治疗组统计
treatment_stats = df.groupby('治疗组').agg({
'患者ID': 'count',
'年龄': 'mean',
'基线指标': 'mean',
'治疗后指标': 'mean',
'改善程度': 'mean',
'改善率': 'mean'
}).rename(columns={'患者ID': '患者数'})
print("\n治疗组统计:")
print(treatment_stats)
# 3. 统计检验
print("\n统计检验:")
print("=" * 50)
# 分离两组数据
experiment_group = df[df['治疗组'] == '实验组']
control_group = df[df['治疗组'] == '对照组']
# t检验:改善程度
t_stat, p_value = stats.ttest_ind(experiment_group['改善程度'], control_group['改善程度'])
print(f"\n改善程度t检验:")
print(f" t统计量: {t_stat:.4f}")
print(f" p值: {p_value:.4f}")
if p_value < 0.05:
print(" 结论: 两组改善程度有显著差异")
else:
print(" 结论: 两组改善程度无显著差异")
# 效应大小(Cohen's d)
def cohens_d(group1, group2):
"""计算Cohen's d"""
n1, n2 = len(group1), len(group2)
var1, var2 = group1.var(), group2.var()
pooled_std = np.sqrt(((n1 - 1) * var1 + (n2 - 1) * var2) / (n1 + n2 - 2))
return (group1.mean() - group2.mean()) / pooled_std
effect_size = cohens_d(experiment_group['改善程度'], control_group['改善程度'])
print(f"\n效应大小(Cohen's d): {effect_size:.4f}")
if abs(effect_size) < 0.2:
print(" 效应大小: 小")
elif abs(effect_size) < 0.5:
print(" 效应大小: 中")
else:
print(" 效应大小: 大")
# 4. 可视化治疗效果
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# 基线指标比较
axes[0, 0].boxplot([experiment_group['基线指标'], control_group['基线指标']],
labels=['实验组', '对照组'])
axes[0, 0].set_title('基线指标比较', fontsize=14, fontweight='bold')
axes[0, 0].set_ylabel('基线指标')
axes[0, 0].grid(True, alpha=0.3)
# 治疗后指标比较
axes[0, 1].boxplot([experiment_group['治疗后指标'], control_group['治疗后指标']],
labels=['实验组', '对照组'])
axes[0, 1].set_title('治疗后指标比较', fontsize=14, fontweight='bold')
axes[0, 1].set_ylabel('治疗后指标')
axes[0, 1].grid(True, alpha=0.3)
# 改善程度比较
axes[1, 0].boxplot([experiment_group['改善程度'], control_group['改善程度']],
labels=['实验组', '对照组'])
axes[1, 0].set_title('改善程度比较', fontsize=14, fontweight='bold')
axes[1, 0].set_ylabel('改善程度')
axes[1, 0].grid(True, alpha=0.3)
# 改善率比较
axes[1, 1].boxplot([experiment_group['改善率'], control_group['改善率']],
labels=['实验组', '对照组'])
axes[1, 1].set_title('改善率比较', fontsize=14, fontweight='bold')
axes[1, 1].set_ylabel('改善率 (%)')
axes[1, 1].grid(True, alpha=0.3)
plt.suptitle('治疗效果比较', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()
# 5. 亚组分析
print("\n亚组分析:")
print("=" * 50)
# 按性别分析
gender_analysis = df.groupby(['治疗组', '性别']).agg({
'改善程度': ['mean', 'std', 'count']
}).round(2)
print("\n按性别分析:")
print(gender_analysis)
# 按年龄分组分析
df['年龄组'] = pd.cut(df['年龄'], bins=[0, 50, 65, 100], labels=['<50', '50-65', '>65'])
age_analysis = df.groupby(['治疗组', '年龄组']).agg({
'改善程度': ['mean', 'std', 'count']
}).round(2)
print("\n按年龄组分析:")
print(age_analysis)
# 6. 生存分析(简化版)
print("\n生存分析:")
print("=" * 50)
# 模拟生存时间
df['生存时间'] = np.random.exponential(365, n_patients) # 天
df['事件发生'] = np.random.choice([0, 1], n_patients, p=[0.3, 0.7]) # 1表示事件发生
# 计算中位生存时间
from lifelines import KaplanMeierFitter
# 注意:需要安装lifelines库: pip install lifelines
try:
kmf = KaplanMeierFitter()
# 实验组
kmf.fit(experiment_group['生存时间'], event_observed=experiment_group['事件发生'], label='实验组')
experiment_median = kmf.median_survival_time_
# 对照组
kmf.fit(control_group['生存时间'], event_observed=control_group['事件发生'], label='对照组')
control_median = kmf.median_survival_time_
print(f"实验组中位生存时间: {experiment_median:.2f} 天")
print(f"对照组中位生存时间: {control_median:.2f} 天")
# 绘制生存曲线
plt.figure(figsize=(10, 6))
kmf.fit(experiment_group['生存时间'], event_observed=experiment_group['事件发生'], label='实验组')
kmf.plot_survival_function()
kmf.fit(control_group['生存时间'], event_observed=control_group['事件发生'], label='对照组')
kmf.plot_survival_function()
plt.title('生存曲线', fontsize=16, fontweight='bold')
plt.xlabel('时间 (天)')
plt.ylabel('生存概率')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
except ImportError:
print("需要安装lifelines库: pip install lifelines")风险因素分析
python
# 风险因素分析示例
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_diabetes
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import classification_report, confusion_matrix
import seaborn as sns
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 1. 加载数据并创建分类目标
diabetes = load_diabetes()
X = pd.DataFrame(diabetes.data, columns=diabetes.feature_names)
y_continuous = pd.Series(diabetes.target, name='目标值')
# 创建分类目标(高风险/低风险)
threshold = y_continuous.median()
y = (y_continuous > threshold).astype(int)
y.name = '风险类别'
print(f"风险类别分布:")
print(y.value_counts())
print(f"\n高风险比例: {y.mean():.2%}")
# 2. 数据预处理
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)
# 标准化
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
# 3. 训练随机森林模型
print("\n随机森林模型训练:")
print("=" * 50)
rf_model = RandomForestClassifier(n_estimators=100, random_state=42)
rf_model.fit(X_train_scaled, y_train)
# 预测
y_pred = rf_model.predict(X_test_scaled)
# 评估
print("\n分类报告:")
print(classification_report(y_test, y_pred, target_names=['低风险', '高风险']))
# 混淆矩阵
cm = confusion_matrix(y_test, y_pred)
plt.figure(figsize=(8, 6))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
xticklabels=['低风险', '高风险'], yticklabels=['低风险', '高风险'])
plt.title('混淆矩阵', fontsize=16, fontweight='bold')
plt.xlabel('预测标签')
plt.ylabel('真实标签')
plt.show()
# 4. 特征重要性分析
print("\n特征重要性分析:")
print("=" * 50)
feature_importance = rf_model.feature_importances_
importance_df = pd.DataFrame({
'特征': diabetes.feature_names,
'重要性': feature_importance
}).sort_values('重要性', ascending=False)
print("\n特征重要性排名:")
print(importance_df)
# 可视化特征重要性
plt.figure(figsize=(10, 6))
plt.barh(importance_df['特征'], importance_df['重要性'], color='skyblue', edgecolor='black')
plt.title('特征重要性', fontsize=16, fontweight='bold')
plt.xlabel('重要性')
plt.ylabel('特征')
plt.gca().invert_yaxis()
plt.grid(True, alpha=0.3, axis='x')
plt.tight_layout()
plt.show()
# 5. 风险因素分析
print("\n风险因素分析:")
print("=" * 50)
# 分析高风险和低风险患者的特征差异
high_risk = X[y == 1]
low_risk = X[y == 0]
# 计算各特征的差异
risk_comparison = pd.DataFrame({
'特征': diabetes.feature_names,
'低风险均值': low_risk.mean().values,
'高风险均值': high_risk.mean().values,
'差异': (high_risk.mean() - low_risk.mean()).values,
'差异百分比': ((high_risk.mean() - low_risk.mean()) / low_risk.mean() * 100).values
})
print("\n风险因素比较:")
print(risk_comparison.sort_values('差异百分比', ascending=False))
# 可视化风险因素比较
fig, axes = plt.subplots(2, 5, figsize=(20, 8))
axes = axes.flatten()
for i, feature in enumerate(diabetes.feature_names):
axes[i].boxplot([low_risk[feature], high_risk[feature]], labels=['低风险', '高风险'])
axes[i].set_title(f'{feature}', fontsize=12, fontweight='bold')
axes[i].grid(True, alpha=0.3)
plt.suptitle('高风险vs低风险患者特征比较', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()
# 6. 风险评分模型
print("\n风险评分模型:")
print("=" * 50)
# 使用特征重要性作为权重
def calculate_risk_score(patient_data, feature_importance):
"""计算风险评分"""
# 标准化
patient_scaled = scaler.transform(patient_data.reshape(1, -1))
# 计算加权得分
risk_score = np.sum(patient_scaled * feature_importance)
return risk_score
# 计算所有患者的风险评分
X_scaled = scaler.transform(X)
risk_scores = np.sum(X_scaled * feature_importance, axis=1)
# 可视化风险评分分布
plt.figure(figsize=(10, 6))
plt.hist(risk_scores[y == 0], bins=30, alpha=0.5, label='低风险', color='green')
plt.hist(risk_scores[y == 1], bins=30, alpha=0.5, label='高风险', color='red')
plt.title('风险评分分布', fontsize=16, fontweight='bold')
plt.xlabel('风险评分')
plt.ylabel('频数')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
# 风险分层
risk_threshold = np.percentile(risk_scores, 75)
high_risk_predicted = risk_scores > risk_threshold
print(f"\n风险阈值: {risk_threshold:.4f}")
print(f"预测高风险比例: {high_risk_predicted.mean():.2%}")
print(f"实际高风险比例: {y.mean():.2%}")课后练习
练习1:患者数据分析
- 分析患者特征分布
- 进行相关性分析
- 识别关键特征
- 可视化分析结果
练习2:疾病预测
- 训练预测模型
- 评估模型性能
- 分析特征重要性
- 优化模型参数
练习3:治疗效果分析
- 进行组间比较
- 进行统计检验
- 计算效应大小
- 进行亚组分析
练习4:风险因素分析
- 识别风险因素
- 构建风险评分
- 进行风险分层
- 验证风险模型
常见问题
Q1: 如何处理医疗数据中的缺失值?
A: 可以尝试:
- 删除缺失值
- 均值/中位数填充
- 多重插补
- 使用能处理缺失值的模型
Q2: 如何保护患者隐私?
A: 可以尝试:
- 数据脱敏
- 匿名化处理
- 差分隐私
- 联邦学习
Q3: 如何处理数据不平衡?
A: 可以尝试:
- 过采样(SMOTE)
- 欠采样
- 调整类别权重
- 使用集成方法
Q4: 如何验证模型的临床意义?
A: 需要考虑:
- 统计显著性
- 临床意义
- 外部验证
- 专家评审
Q5: 医疗数据分析需要什么技能?
A: 需要掌握:
- 医学知识
- 统计分析
- 机器学习
- 数据可视化
下一步学习
完成今天的学习后,建议你:
- 练习处理真实的医疗数据
- 掌握疾病预测方法
- 学习治疗效果分析
- 准备进入Day 15的学习:完整项目实战与部署
明天我们将学习完整的项目实战和部署,这是数据分析的最终目标。