pip install pyecharts
## 库导入及其数据读取
import pandas as pd
import numpy as np 
import matplotlib.pyplot as plt
import matplotlib as mpl
from pyecharts.charts import *
from pyecharts import options as opts
import warnings
warnings.filterwarnings('ignore') #忽略警告信息
mpl.rcParams['font.sans-serif']=['SimHei']
mpl.rcParams['axes.unicode_minus']=False
data = pd.read_csv('Medicaldataset.csv')
data.head()
AgeGenderHeart rateSystolic blood pressureDiastolic blood pressureBlood sugarCK-MBTroponinResult
06416616083160.01.800.012negative
1211949846296.06.751.060positive
25516416077270.01.990.003negative
36417012055270.013.870.122positive
45516411265300.01.080.003negative

数据预处理

#查看数据信息
data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1319 entries, 0 to 1318
Data columns (total 9 columns):
 #   Column                    Non-Null Count  Dtype  
---  ------                    --------------  -----  
 0   Age                       1319 non-null   int64  
 1   Gender                    1319 non-null   int64  
 2   Heart rate                1319 non-null   int64  
 3   Systolic blood pressure   1319 non-null   int64  
 4   Diastolic blood pressure  1319 non-null   int64  
 5   Blood sugar               1319 non-null   float64
 6   CK-MB                     1319 non-null   float64
 7   Troponin                  1319 non-null   float64
 8   Result                    1319 non-null   object 
dtypes: float64(3), int64(5), object(1)
memory usage: 92.9+ KB

没有空缺数据

#删除重复值
data.duplicated().sum()
0
# 为数据集特征创建映射字典,中文便于理解
feature_map = {
    'Age': '年龄',
    'Heart rate': '心率',
    'Systolic blood pressure': '收缩压',
    'Diastolic blood pressure': '舒张压',
    'Blood sugar': '血糖',
    'CK-MB': '肌酸激酶同工酶',
    'Troponin': '肌钙蛋白'
}

#绘制每个特征的统计分布图,查找是否存在离群点(异常值)
plt.figure(figsize=(20, 10))
for i, (col, col_name) in enumerate(feature_map.items(), 1):
    plt.subplot(2, 4, i) #创建子图
    plt.boxplot(data[col]) #画箱形图
    plt.title(f'{col_name}的箱线图', fontsize=14)
    plt.ylabel('数值', fontsize=12)
    plt.grid(axis='y', linestyle='--', alpha=0.7)

plt.tight_layout()
plt.show()

在这里插入图片描述#### 通过观察发现:心率、收缩压、舒张压存在极端异常值,虽然年龄、血糖、肌酸激酶同工酶、肌钙蛋白也存在离群点,但是这些离群点可能是真实反映,故不处理(急性心肌梗死情况下,肌酸激酶同工酶和肌钙蛋白会特别高),只处理那些极端异常值,删除心率大于1000的那个异常值点,删除收缩压小于50的那个点,删除舒张压大于140的那个点。

# 处理前的数据量
print(f"处理前数据量: {len(data)}")

# 删除心率大于1000的异常值
data = data[data['Heart rate'] <= 1000]

# 删除收缩压小于50的异常值
data = data[data['Systolic blood pressure'] >= 50]

# 删除舒张压大于140的异常值
data = data[data['Diastolic blood pressure'] <= 140]

print(f"处理后数据量: {len(data)}")
处理前数据量: 1319
处理后数据量: 1314
#检查舒张压和收缩压的关系,看看是否存在舒张压大于收缩压的情况。
wrong_bloodpressure = data[data['Diastolic blood pressure'] > data['Systolic blood pressure']]
print(f"发现舒张压大于收缩压的记录数: {len(wrong_bloodpressure)}")
发现舒张压大于收缩压的记录数: 6
考虑到可能是录入时候这6条数据录入反了,所以进行调换处理。
# 获取这些记录的索引
wrong_bloodpressure_index = wrong_bloodpressure.index
# 调换这些记录的收缩压和舒张压值
data.loc[wrong_bloodpressure_index, ['Systolic blood pressure', 'Diastolic blood pressure']] = data.loc[wrong_bloodpressure_index, ['Diastolic blood pressure', 'Systolic blood pressure']].values
#再次画图检查一下分布情况

plt.figure(figsize=(20, 10))
for i, (col, col_name) in enumerate(feature_map.items(), 1):
    plt.subplot(2, 4, i)
    plt.boxplot(data[col])
    plt.title(f'处理后{col_name}的箱线图', fontsize=14)
    plt.ylabel('数值', fontsize=12)
    plt.grid(axis='y', linestyle='--', alpha=0.7)

plt.tight_layout()
plt.show()

在这里插入图片描述

数据分析

data.describe().T
countmeanstdmin25%50%75%max
Age1314.056.19863013.64558314.00047.00058.00065.0000103.0
Gender1314.00.6598170.4739510.0000.0001.0001.00001.0
Heart rate1314.075.98782315.28689320.00064.00074.00085.0000135.0
Systolic blood pressure1314.0127.31659125.88183265.000110.000124.000143.0000223.0
Diastolic blood pressure1314.072.08751913.79799138.00062.00071.00080.0000128.0
Blood sugar1314.0146.79048775.01365935.00098.000116.000170.0000541.0
CK-MB1314.015.32287746.4084360.3211.6602.8505.8175300.0
Troponin1314.00.3611741.1565100.0010.0060.0140.085010.3
#可视化分析

#1.患者年龄分布
plt.figure(figsize=(20,15))
plt.subplot(3,3,1)
data['Age'].plot(kind='kde',color='red',label='核密度图')#绘制核密度图
data['Age'].plot(kind='hist',bins=12,secondary_y=True)#绘制直方图,,secondary_y=True添加第二个y轴显示人数
#plt.hist(data['Age'],bins=12)
plt.title('患者年龄分布')
plt.xlabel('年龄(岁)')
plt.ylabel('人数')
plt.grid(axis='y',linestyle='--')

#2.患者性别分布(饼图)
gender_counts=data['Gender'].value_counts() #数据统计
plt.subplot(3,3,2)
plt.pie(gender_counts,labels=['男','女'],autopct="%1.1f%%",startangle=75,colors=['#66b3ff','#ff9999'])
plt.title('患者性别分布')
plt.axis('equal') #使得饼图为圆形

#3.绘制患者心率分布
plt.subplot(3,3,3)
data['Heart rate'].plot(kind='kde',color='red',label='核密度图')#绘制核密度图
data['Heart rate'].plot(kind='hist',bins=12,secondary_y=True)#绘制直方图
#plt.hist(data['Heart rate'],bins=12)
plt.title('患者心率分布')
plt.xlabel('心率(次/分钟)')
plt.ylabel('人数')
plt.grid(axis='y',linestyle='--')

#4.绘制患者收缩压分布
plt.subplot(3,3,4)
data['Systolic blood pressure'].plot(kind='kde',color='red',label='核密度图')#绘制核密度图
data['Systolic blood pressure'].plot(kind='hist',bins=12,secondary_y=True)#绘制直方图
#plt.hist(data['Systolic blood pressure'],bins=12)
plt.title('患者收缩压分布')
plt.xlabel('收缩压(mmHg)')
plt.ylabel('人数')
plt.grid(axis='y',linestyle='--')

#5.绘制患者舒张压分布
plt.subplot(3,3,5)
data['Diastolic blood pressure'].plot(kind='kde',color='red',label='核密度图')#绘制核密度图
data['Diastolic blood pressure'].plot(kind='hist',bins=12,secondary_y=True)#绘制直方图
#plt.hist(data['Diastolic blood pressure'],bins=12)
plt.title('患者舒张压分布')
plt.xlabel('舒张压(mmHg)')
plt.ylabel('人数')
plt.grid(axis='y',linestyle='--')

#6.患者血糖分布
plt.subplot(3,3,6)
data['Blood sugar'].plot(kind='hist',bins=12,secondary_y=True)#绘制直方图
data['Blood sugar'].plot(kind='kde',color='red',label='核密度图')#绘制核密度图
#plt.hist(data['Blood sugar'],bins=12,density=True)
plt.title('患者血糖分布')
plt.xlabel('血糖(mg/dl)')
plt.ylabel('人数')
plt.grid(axis='y',linestyle='--')

#7.患者肌酸激酶同工酶水平
plt.subplot(3,3,7)
data['CK-MB'].plot(kind='kde',color='red',label='核密度图')#绘制核密度图
data['CK-MB'].plot(kind='hist',bins=12,secondary_y=True)#绘制直方图
#plt.hist(data['Systolic blood pressure'],bins=12)
plt.title('患者肌酸激酶同工酶水平')
plt.xlabel('肌酸激酶同工酶')
plt.ylabel('人数')
plt.grid(axis='y',linestyle='--')

#8.患者肌钙蛋白水平
plt.subplot(3,3,8)
data['Troponin'].plot(kind='kde',color='red',label='核密度图')#绘制核密度图
data['Troponin'].plot(kind='hist',bins=12,secondary_y=True)#绘制直方图
plt.title('患者肌钙蛋白水平')
plt.xlabel('肌钙蛋白')
plt.ylabel('人数')
plt.grid(axis='y',linestyle='--')

#9.患者患心脏病比例(饼图)
Result_counts=data['Result'].value_counts() #数据统计
plt.subplot(3,3,9)
plt.pie(Result_counts,labels=['患病','未患病'],autopct="%1.1f%%",startangle=75,colors=['#66b3ff','#ff9999'])
plt.title('患者患心脏病比例')
plt.axis('equal') #使得饼图为圆形
(-1.0999995255527086,
 1.0999989954709557,
 -1.0999999092452373,
 1.0999984088108516)

在这里插入图片描述

患者基础人口特征:\ 性别分布:男性(1)共870人,女性(0)共449人\ 年龄分布:年龄范围14-103岁,平均56.2岁,集中在55-65岁区间

患者生理指标:\ 心率:平均76次/分钟,主要分布在65-85次/分钟\ 血压:收缩压平均127.3mmHg,舒张压平均72.1mmHg\ 血糖:平均146.8mg/dl,呈右偏分布
患者心脏指标:\ CK-MB(肌酸激酶同工酶):平均15.3,大多数患者值较低\ 肌钙蛋白:平均0.36,大部分患者值集中在较低区间
诊断结果:\ 阳性(positive)患者807例,阴性(negative)患者507例

心脏病影响因素分析

#可视化分析
fig=plt.figure(figsize=(20,16))
plt.subplots_adjust(wspace=0.3,hspace=0.4)#调整子图的间隔

#1.年龄与诊断结果关系
ax1=plt.subplot(3,3,1)
positive_ages = data[data['Result'] == 'positive']['Age']
negative_ages = data[data['Result'] == 'negative']['Age']
plt.boxplot([positive_ages, negative_ages], labels=['Positive', 'Negative'])
#data.boxplot(column='Age',by='Result',ax=ax1)#这种API需要data为DF格式,通过指定列column和by分组
plt.title('年龄与心脏病诊断结果的关系')
plt.xlabel('诊断结果')
plt.ylabel('年龄')

# 2.性别与诊断结果关系
positive_data=data.loc[data['Result']=='positive']
gender_positive_counts=positive_data['Gender'].value_counts()

plt.subplot(3, 3, 2)
plt.pie(gender_positive_counts,labels=['男','女'],autopct="%1.1f%%",startangle=75,colors=['#66b3ff','#ff9999'])
plt.title('性别与心脏病诊断结果的关系')


# 3. 心率与诊断结果关系
ax3 = plt.subplot(3, 3, 3)
positive_Heartrate = data[data['Result'] == 'positive']['Heart rate']
negative_Heartrate = data[data['Result'] == 'negative']['Heart rate']
plt.boxplot([positive_Heartrate, negative_Heartrate], labels=['Positive', 'Negative'])
ax3.set_title('心率与心脏病诊断结果的关系', fontsize=14)
ax3.set_xlabel('诊断结果', fontsize=12)
ax3.set_ylabel('心率 (次/分钟)', fontsize=12)

# 4. 收缩压与诊断结果关系
ax4 = plt.subplot(3, 3, 4)
data.boxplot(column='Systolic blood pressure',by='Result',ax=ax4)
ax4.set_title('收缩压与心脏病诊断结果的关系', fontsize=14)
ax4.set_xlabel('诊断结果', fontsize=12)
ax4.set_ylabel('收缩压 (mmHg)', fontsize=12)

# 5. 舒张压与诊断结果关系
ax5 = plt.subplot(3, 3, 5)
data.boxplot(column='Diastolic blood pressure',by='Result',ax=ax5)
ax5.set_title('舒张压与心脏病诊断结果的关系', fontsize=14)
ax5.set_xlabel('诊断结果', fontsize=12)
ax5.set_ylabel('舒张压 (mmHg)', fontsize=12)

# 6. 血糖与诊断结果关系
ax6 = plt.subplot(3, 3, 6)
data.boxplot(column='Blood sugar',by='Result',ax=ax6)
ax6.set_title('血糖与心脏病诊断结果的关系', fontsize=14)
ax6.set_xlabel('诊断结果', fontsize=12)
ax6.set_ylabel('血糖 (mg/dl)', fontsize=12)

# 7. CK-MB与诊断结果关系
ax7 = plt.subplot(3, 3, 7)
positive_CKMB = data[data['Result'] == 'positive']['CK-MB']
negative_CKMB = data[data['Result'] == 'negative']['CK-MB']
plt.boxplot([positive_CKMB, negative_CKMB], labels=['Positive', 'Negative'])
#data.boxplot(column='CK-MB',by='Result',ax=ax7)
ax7.set_title('CK-MB与心脏病诊断结果的关系', fontsize=14)
ax7.set_xlabel('诊断结果', fontsize=12)
ax7.set_ylabel('CK-MB', fontsize=12)

# 8. 肌钙蛋白与诊断结果关系
ax8 = plt.subplot(3, 3, 8)
data.boxplot(column='Troponin',by='Result',ax=ax8)
ax8.set_title('肌钙蛋白与心脏病诊断结果的关系', fontsize=14)
ax8.set_xlabel('诊断结果', fontsize=12)
ax8.set_ylabel('肌钙蛋白', fontsize=12)

plt.tight_layout()
plt.show()

在这里插入图片描述

心脏病患者的肌酸激酶同工酶和肌钙蛋白显著高于未患心脏病的人,而且高龄可能也会导致心脏病,男性患心脏病的比例高于女性,其他特征差异不是非常明显。

分类模型构建与预测

数据预处理

将目标变量转为01变量,并且把连续变量进行标准化处理,由于目标变量的不平衡程度相对温和(阳性61.4%,阴性38.6%),未达到需要强制平衡的临界水平(差异在60%以上才考虑平衡样本),将处理后的数据进行划分
# 将"positive"/"negative"转换为1/0
data['Result_Binary'] = (data['Result'] == 'positive').astype(int)
features = ['Gender','Age', 'CK-MB', 'Troponin']
# 定义特征和目标变量
x = data[features].copy()
y = data['Result_Binary']

# 只对连续变量标准化
continuous_vars = ['Age', 'CK-MB', 'Troponin']
# 先把连续变量列转为float类型
x[continuous_vars] = x[continuous_vars].astype(float)
scaler = StandardScaler()#创建标准化模型
x.loc[:, continuous_vars] = scaler.fit_transform(x[continuous_vars])#进行标准化处理
# 划分训练集和测试集(80%/20%)
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=15, stratify=y)

逻辑回归模型

模型预测基本步骤:\ 1.创建模型:模型= 模型函数(参数配置)\ 2.训练模型:模型.fit(x训练集,y训练集)\ 3.模型预测:模型.predict(x测试集)\ 4.模型评估:\ 模型报告:classification_report(y_test, y_pred)\ 混淆矩阵:confusion_matrix(y_test, y_pred))\ ROC曲线:roc_curve(y_test, y_prob)\

由于所有模型的训练、预测、评估都是一样的,古可以定义成一个函数

def evaluate_model(model, model_name, X_train, X_test, y_train, y_test):  #模型对象、模型名称,x训练集,y训练集,x测试集
    # 训练模型
    model.fit(X_train, y_train)
    
    # 预测
    y_pred = model.predict(X_test)
    
    # 打印分类报告
    print(f"=== {model_name} 模型评估 ===")
    print(classification_report(y_test, y_pred))#包含准确率、召回率、f1测度、支持率
    
    # 绘制混淆矩阵
    cm = confusion_matrix(y_test, y_pred) #返回混淆矩阵
    plt.figure(figsize=(8, 6))
    plt.imshow(cm,cmap='hot',interpolation='nearest')#热力图
    plt.colorbar()#视觉映射
    #添加标签
    for i in range(2):
        for j in range(2):
            plt.text(j, i, f'{cm[i, j]:.2f}', ha='center', va='center', color='black')
    plt.title(f'{model_name}模型混淆矩阵')
    plt.show()
    
    # 绘制ROC曲线
    if hasattr(model, "predict_proba"):#hasattr用于检查对象是否具有指定的属性
        y_prob = model.predict_proba(X_test)[:, 1]
    else:  # 对于SVM等没有predict_proba方法的模型
        y_prob = model.decision_function(X_test) if hasattr(model, "decision_function") else y_pred
    
    fpr, tpr, _ = roc_curve(y_test, y_prob)  #roc曲线
    roc_auc = auc(fpr, tpr) #auc面积
    
    plt.figure(figsize=(8, 6))
    plt.plot(fpr, tpr, color='darkorange', lw=2, label='ROC曲线(面积 = %0.2f)' % roc_auc)
    plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')
    plt.xlabel('假阳率')
    plt.ylabel('真阳率')
    plt.title(f'{model_name}模型ROC曲线')
    plt.legend(loc="lower right")
    plt.show()
    
    return model, roc_auc
结论
本项目基于对心脏病发作数据集的系统分析,得出以下主要结论:

人群特征:患者年龄分布广泛,平均年龄56.2岁,男性比例高于女性,且男性患心脏病的风险显著高于女性。\ 生理与生化指标:心脏病患者的肌酸激酶同工酶(CK-MB)和肌钙蛋白(Troponin)水平均显著高于未患病者,年龄也是影响心脏病的重要因素。心率、血压、血糖等常规生理指标在两组间差异不显著。

模型预测:多种机器学习模型均能较好地区分心脏病患者与非患者,其中树模型(决策树、随机森林、XGBoost)表现优于逻辑回归和SVM,具有较高的预测准确性。\ 实际意义:本研究结果为心脏病的早期筛查和风险评估提供了数据支持,提示临床在关注传统生理指标的同时,应重视心脏生化指标的检测与分析。

一、分类模型概述
分类模型的目标是基于特征变量预测样本的类别标签。常见的分类算法包括:
传统算法:逻辑回归、决策树、随机森林、支持向量机 (SVM)、K 近邻 (KNN)等。
集成学习:AdaBoost、Gradient Boosting (XGBoost、LightGBM、CatBoost) 等。
神经网络:多层感知机 (MLP)、卷积神经网络 (CNN)、循环神经网络 (RNN) 等。
二、分类模型构建流程

  1. 数据准备
    首先需要准备训练数据,包括特征矩阵X和标签向量y。
  2. 模型选择与训练
    根据数据特点和任务需求选择合适的模型。
  3. 模型评估
    使用多种评估指标全面评估模型性能。
  4. 超参数调优
    使用网格搜索或随机搜索找到最优参数组合。
    三、预测流程
    训练好模型后,可对新数据进行预测。
    四、处理不平衡数据
    当类别分布不均时,需采取特殊策略。
    五、集成多个分类器
    通过投票或堆叠策略集成多个模型,提高预测性能。
    1.逻辑回归:
    逻辑回归模型的核心是Sigmoid函数,它将线性回归模型的输出映射到(0,1)区间内,表示为概率。这个概率可以用来预测样本属于某个类别的可能性。逻辑回归虽然名字中有“回归”二字,但它实际上是用于分类的,特别是二分类问题
    在这里插入图片描述
    2.决策树:
    决策树模型通过一系列规则对数据进行分类。它包括根节点、内部节点和叶节点。根节点是决策树的起始点,内部节点表示特征属性,叶节点代表决策结果。构建决策树时,需要考虑如何选择最佳特征和如何进行树的剪枝以避免过拟合。
    在这里插入图片描述
    3.随机森林:
    随机森林是一种流行的机器学习算法,它通过构建多个决策树并输出分类的模式或者回归的平均预测值。在Python中,可以使用sklearn.ensemble模块中的RandomForestClassifier或RandomForestRegressor类来实现随机森林模型的训练和预测。

在这里插入图片描述
4.支持向量机

SVM的目标是在特征空间中找到一个最优的超平面,以最大化不同类别数据点之间的间隔。这个超平面是由支持向量定义的,即距离超平面最近的数据点。SVM通过最大化这个间隔来构建分类器,从而提高模型的泛化能力。
在这里插入图片描述
5.K 近邻 (KNN):
邻近算法,或者说K最邻近(KNN,K-NearestNeighbor)分类算法是数据挖掘分类技术中最简单的方法之一。
在这里插入图片描述

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐