python 心脏病可视化和分类预测

一、 问题分析的背景和意义

背景:心脏病是人类健康的头号杀手,全世界1/3的人口死亡是心脏病引起的。而我国,每年有几十万人死于心脏病。如果可以通过提取人体相关的体测指标,通过数据挖掘方式来分析不同特征对于心脏病的影响,将对预防心脏病起到至关重要的作用。

意义:此数据集可以用于分析患者患有心脏病是否与本身患有的其他疾病有关系,为预测心脏病提供决策支持。例如,通过分析心脏病分类预测数据集,我们可以得出结论,年龄越大、不运动的患者得心脏病的可能性越高,或者患者本身患有的某些疾病与心脏病之间存在显著的相关性等。

二、 数据来源

数据文件heart.csv已经放入网盘里,需要可自行下载
链接:https://pan.baidu.com/s/1xjMOCrEzLLHkARP90q2iAw
提取码:2323

数据集说明:心脏病分类预测数据集是指针对心脏病患者分类的相关数据集。这类数据集通常包含了关于患者的基本信息,包括年龄、性别、静息血压、胆固醇等,以及患者是否患有其他疾病的相关信息,包括心绞痛、糖尿病、地中海贫血等。

三、 数据预处理

1.加载第三方库

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style('darkgrid')
import warnings
warnings.filterwarnings('ignore')
import matplotlib
from sklearn import metrics
from scipy.stats import pearsonr
from sklearn.metrics import accuracy_score
from sklearn.metrics import classification_report
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False
print('第三方库加载成功')

2.读取数据文件,展示数据信息

heart = pd.read_csv('heart.csv')
heart.head()

3.将数据集复制一份,查看数据类型,数据大小,描述性统计

data = heart.copy()
data.dtypes
data.shape
data.describe()



4.查看重复值和缺失值

data.drop_duplicates()
data.isnull().sum()


删除数据中的重复值后,数据大小与先前的一样,说明没有重复值。

查询的各列缺失值的数目都为0,没有数据缺失。

5.查看年龄列的异常值判定

X = 'age'
print(X+':')
mean1 = data[X].quantile(q = 0.25)  # 下四分位差
mean2 = data[X].quantile(q = 0.75)  # 上四分位差
mean3 = mean2 - mean1  # 中位差
topnum2 = mean2 + 1.5*mean3
bottomnum2 = mean2 - 1.5*mean3
print("正常值的范围:", topnum2, bottomnum2)
print("是否存在超出正常范围的值:", any(data[X]>topnum2))
print("是否存在小于正常范围的值:", any(data[X]<bottomnum2))


使用上四中位数和下四中位数进行异常值判定,得出正常值的范围在40-81岁之间,由于这一属性列是年龄,只能说明大部分的值在40-81岁之间,但是也有在这些年龄以外的情况,所以超过了这个区间的年龄不算异常值。

6.特征编码

# 特征编码
size_mapping = {'male': 1,'female': 0} 
data['sex'] = data['sex'].map(size_mapping)
size_mapping1 = {'normal': 1,'fixed defect': 2,'reversable defect':3} 
data['thal'] = data['thal'].map(size_mapping1)
data

对数据集进行特征编码,将数据集里的文本型数据转换为数值型数据。

四、数据可视化分析(使用pyecharts库)

1.是否患病情况数量 – 饼图

data['target'].value_counts()

from pyecharts import Pie
attr = ['正常','患病']
data = [138,165]
pie = Pie("是否患病情况数量")
# 是否直接显示label信息
pie.add("", attr, data, is_label_show=True)
pie.render("issick.html")

from IPython.display import IFrame
IFrame(src='./issick.html', width=800, height=450)


没有患心脏病人数是:138,没有患心脏病的比例是: 45.54%;患有心脏病的人数是:165, 患有心脏病的比例是:54.46%。数据集里两类人数相差不是很大。

2.不同年龄段的人患心脏病的情况 – 条形图

# 由于年龄过于分散,将年龄拆分成不同段
age_distDf = pd.DataFrame()

# 用pd.cut() 将年龄进行分割
age_distDf['age_range']  = pd.cut(x = data['age'],
                                  bins = [0,18,40,60,100],
                                  include_lowest = True,right=False,
                                  labels = ['儿童','青年','中年','老年'])

# 将原数据集的target合并到age_distDf中
age_distDf = pd.concat([age_distDf['age_range'],data['target']],axis=1)
age_distDf.head()

at = age_distDf.groupby(["age_range","target"]).agg({'target':'count'})
at.rename(columns={'target':'amount'},inplace=True)
at = at.reset_index()
print(at)

from pyecharts import Bar
columns = ['儿童','青年','中年','老年']
data1 = [0, 4, 82, 52]
data2 = [0, 12, 115, 38]
bar = Bar("不同年龄段的人患心脏病的情况") 
bar.add("未得心脏病的人数", columns, data1) 
bar.add("患心脏病的人数", columns, data2) 
bar.render("agesick.html")

from IPython.display import IFrame
IFrame(src='./agesick.html', width=800, height=450)


患心脏的人群里,儿童为0人,青年为12人,中年为115人,老年为38人;不患心脏病的人群里,儿童为0人,青年为4人,中年为82人,老年为52人。患心脏病的人群里,中年人和老年人都比较多。

3.不同年龄段人群患病情况 – 3d柱状图

from pyecharts import Bar3D

bar3d = Bar3D("3D 柱状图示例", width=1200, height=600)
x_axis = [
    '儿童', '青年',
    '中年', '老年'
    ]
y_axis = [
    '健康', '患病'
    ]
data = [
    [0, 0, 0], [1, 0, 0],
    [0, 1, 4], [1, 1, 12],
    [0, 2, 82], [1, 2, 115],
    [0, 3, 52], [1, 3, 38]
    ]
range_color = ['#313695', '#4575b4']
bar3d.add(
    "",
    x_axis,
    y_axis,
    [[d[1], d[0], d[2]] for d in data],
    is_visualmap=True,
    visual_range=[0, 20],
    visual_range_color=range_color,
    grid3d_width=200,
    grid3d_depth=80,
)
bar3d.render("bar3d.html")

from IPython.display import IFrame
IFrame(src='./bar3d.html', width=1000, height=600)


青年人的患病率高于老年人的患病率,很大程度上和现代青年人压力大,熬夜,缺乏运动等不健康的生活方式有关。

4.不同性别人群患病情况 – 漏斗图

atyy = data.groupby(["sex","target"]).agg({'target':'count'})
atyy.rename(columns={'target':'amount'},inplace=True)
atyy = atyy.reset_index()
print(atyy)

from pyecharts import Funnel

x_movies_name = ["女性正常", "女性患心脏病", "男性患心脏病病", "男性正常"]
y_16 = [24, 72, 93, 114]
funnel = Funnel("不同性别人群患病情况")
funnel.add("不同性别人群患病情况", x_movies_name, y_16)
funnel.render("funnel.html")

from IPython.display import IFrame
IFrame(src='./funnel.html', width=900, height=450)


从上图可以看出,女性正常的人数有24人,女性患心脏病的人数有72人;男性正常的人数有114人,男性患心脏病的人数有93人。总的来说,女性患心脏病的比例高于男性。

5.不同胸痛类型的患病情况 – 折线图

atcp = data.groupby(["cp","target"]).agg({'target':'count'})
atcp.rename(columns={'target':'amount'},inplace=True)
atcp = atcp.reset_index()
print(atcp)

from pyecharts import Page
from pyecharts import Line
page = Page()
attr = ['典型心绞痛', '非典型心绞痛', '非心绞痛', '没有症状']
line1 = Line("不同胸痛类型与患病的关系")
line1.add(
    "正常人群",
    attr,
    [104,9,18,7],
    mark_point=["max", "min"],
    mark_line=["average"],
    mark_point_symbol="arrow",
    mark_point_textcolor="#40ff27",
    mark_point_symbolsize=25,
)
line1.add(
    "患心脏病人群",
    attr,
    [39,41,69,16],
    mark_point=["max", "min"],
    mark_line=["average"],
    yaxis_formatter="",
    mark_point_symbol="diamond",
    mark_point_symbolsize=25,
)
page.add(line1)
page.render("line.html")

from IPython.display import IFrame
IFrame(src='./line.html', width=800, height=450)


典型性心绞痛患者得心脏病的概率比较低,只是单纯的心绞痛,并非心脏病,而其他类心绞痛患者得心脏病的几率比价高,说明心脏病和心绞痛类型存在一定的关系。

6.心率与静息血压的关系 – 散点图

tandt = data[['trestbps','thalach']]
print(tandt)
from pyecharts import Scatter   #散点图
v1 =tandt.trestbps
v2 =tandt.thalach
scatter =Scatter("心率与静息血压的关系", title_pos='center', background_color='white', title_top='90%')
scatter.add("静息血压", v1, v2)
scatter.add("心率", v1[::-1], v2)
scatter.show_config()
scatter.render("scatter.html")

from IPython.display import IFrame
IFrame(src='./scatter.html', width=800, height=450)


由上图可以看出,心率和静息血压之间相关性很高,数据点都比较集中,横轴代表静息血压,大部分的值都位于80-200之间,纵轴代表心率,大部分的值也是位于80-200之间。

7.运动与心脏病的关系 – 阶梯图

atex = data.groupby(["exang","target"]).agg({'target':'count'})
atex.rename(columns={'target':'amount'},inplace=True)
atex = atex.reset_index()
print(atex)

from pyecharts import Line

data_x = ['不运动的正常人群', '不运动的患病人群', '运动的正常人群', '运动的患病人群']
data_y = [62,142,76,23]

line = Line(title='运动与心脏病关系阶梯图')
line.add(name='Sum', x_axis=data_x, y_axis=data_y, is_step=True, is_label_show=True, yaxis_min=15, yaxis_max=160, legend_text_color='red')
line.render('step.html')

from IPython.display import IFrame
IFrame(src='./step.html', width=800, height=450)


运动引起的心绞痛诊断为心脏病的概率比较低。所以只是运动是有心绞痛的现象,首先考虑可能是其他疾病,但是如果本身有心脏病,要听从医嘱静养,高强度的运动还是会引起心绞痛的。

8.地中海贫血的血液疾病与心脏病的关系 – 环形图

t = data.groupby(["thal","target"]).agg({'target':'count'})
t.rename(columns={'target':'amount'},inplace=True)
t = t.reset_index()
print(t)

from pyecharts import Pie
from pyecharts import options as opts
c = Pie()
L1 = ["thal=1,正常","thal=1,患心脏病","thal=2,正常","thal=2,患心脏病","thal=3,正常","thal=3,患心脏病"]
num = [13,7,36,130,89,28]
c.add("",attr = L1,value =num,radius=["40%","75%"])
# c.set_global_opts(title_opts=opts.TitleOpts(title='Pie圆环'),
#                 legend_opts=opts.LegendOpts(orient='vertical',pos_top='5%',pos_left="2%"))
# c.set_series_opts(label_opts=opts.LabelOpts(formatter="{b}:{c}"))
c.render("c.html")

from IPython.display import IFrame
IFrame(src='./c.html', width=800, height=450)


当地中海贫血的类型为2,也就是固定缺陷,并且属于患心脏病的人群这一类最多,为130人,这可能说明地中海贫血与患心脏病之间有一定的联系。

9.运动高峰的心电图各类型数量情况 – 线性_闪烁图

data['slope'].value_counts()

from pyecharts import Line,EffectScatter,Overlap
attr = ['上坡','平坦','下坡']
v1 = [21,140,142]
line = Line('线性_闪烁图示例')
line.add('',attr,v1,is_random = True)

es = EffectScatter()
es.add('',attr,v1,effect_scale=8)   #闪烁
overlop = Overlap()
overlop.add(line)                   #必须先添加line,在添加es
overlop.add(es)
overlop.render('./line-es01.html')

from IPython.display import IFrame
IFrame(src='./line-es01.html', width=800, height=450)


当运动高峰的心电图的类型为上坡,为21人,当运动高峰的心电图的类型为平坦,为140人,当运动高峰的心电图的类型为下坡,为142人.

10.不同性别的人的空腹血糖情况 – 线性_柱状图

s = data.groupby(["sex","fbs"]).agg({'fbs':'count'})
s.rename(columns={'fbs':'amount'},inplace=True)
s = s.reset_index()
print(s)

from pyecharts import Bar,Line,Overlap
attr = ['女性血糖<120mg/d','女性血糖>120mg/d','男性血糖<120mg/d','男性血糖>120mg/d']
v1 = [84,12,174,33]
v2 = [84,12,174,33]
bar = Bar('Line - Bar示例')
bar.add('bar',attr,v1)
line = Line()
line.add('line',attr,v2)

overlop = Overlap()
overlop.add(bar)
overlop.add(line)
overlop.render('./line-bar01.html')

from IPython.display import IFrame
IFrame(src='./line-bar01.html', width=800, height=450)


当女性血糖<120mg/d时人数为84人,当女性血糖>120mg/d时为12人,当男性血糖<120mg/d时为174人,当男性血糖>120mg/d时为33人。由此可以看出,数据集里的男性或者女性的血糖普遍都是低于120mg/d。

11.查看不同特征之间的相关关系

# 查看不同特征之间的相关关系
plt.figure(figsize=(12,10))
corr =  data.corr()
sns.heatmap(data=corr,annot=True, square=True,fmt='.2f')
plt.show()

五、建模预测

1.独热处理

# 采用get_dummies()编码方式处理非连续性分类数据
cp_dummies= pd.get_dummies(data['cp'],prefix = 'cp')
restecg_dummies =  pd.get_dummies(data['restecg'],prefix='restecg')
slope_dummies =  pd.get_dummies(data['slope'],prefix='slope')
thal_dummies = pd.get_dummies(data['thal'],prefix='thal')

# 将原数据中经过独热编码的列删除
heart_new =  data.drop(['cp','restecg','slope','thal'],axis=1)
heart_new = pd.concat([heart_new,cp_dummies,restecg_dummies,slope_dummies,thal_dummies],axis=1)
heart_new.head()

2.分离出数据和标签

# 分离出数据和标签
label =  heart_new['target']
data =  heart_new.drop('target',axis=1)
data.shape

3.归一化处理

# 数据集合的不同特征之间数据相差有点大,对于SVM、KNN等算法,会产生权重影响,因此需要归一化处理数据
from sklearn.preprocessing import StandardScaler
standardScaler = StandardScaler()
standardScaler.fit(data)
data =  standardScaler.transform(data)

4.拆分训练集,测试集

# 拆分训练集,测试集
from sklearn.model_selection import train_test_split
train_X,test_X,train_y,test_y = train_test_split(data,label,random_state=3)
train_X.shape

5.KNN近邻法

# 构建模型
from sklearn.neighbors import KNeighborsClassifier
knn =  KNeighborsClassifier()
#训练数据
knn.fit(train_X,train_y)
# 预测数据
knn_pred_y = knn.predict(test_X)
# 评估模型
knn.score(train_X,train_y)

knn.score(test_X,test_y)

accuracy_score(test_y,knn_pred_y)


模型的测试集得分和准确率为86.842%

6.使用网格搜索寻找更好的模型参数

from sklearn.model_selection import GridSearchCV
knn =  KNeighborsClassifier()
param_test =  [
    {'n_neighbors':[i for i in range(1,31)],
    'weights':['uniform']},

    {'n_neighbors':[i for i in range(1,21)],
    'weights':['distance'],
    'p':[i for i in range(1,6)]}
]
knn_gv = GridSearchCV(estimator = knn,param_grid=param_test,cv=5)
knn_gv.fit(train_X,train_y)

# 最优参数
knn_gv.best_params_

knn =  KNeighborsClassifier(n_neighbors=16,p=3,weights='distance')

#训练数据
knn.fit(train_X,train_y)

# 预测数据
knn_pred_y = knn.predict(test_X)

# 评估模型
knn.score(train_X,train_y)


使用模型查找出的KNN最优参数为n_neighbors=16,p=3,weights=distance.使用最优参数,得出的模型训练集得分为1。

7.查看模型准确率,召回率

# 预测数据
knn_pred_y = knn_gv.predict(test_X)

# 查看主要分类指标文本报告
print(classification_report(test_y,knn_pred_y))
print(knn_gv.score(test_X,test_y))


模型的精准率,召回率,F1值分别为0.86,0.86,0.87。

8.计算评价指标

MSE = metrics.mean_squared_error(test_y,knn_pred_y)
RMSE = np.sqrt(MSE)
R = pearsonr(test_y, knn_pred_y)[0]
std = np.std(test_y-knn_pred_y)
RMSE,R,std


模型的RMSE为0.3627,R为0.7212,std为0.3627。

物联沃分享整理
物联沃-IOTWORD物联网 » python 心脏病可视化和分类预测

发表评论