基于pyecharts的心血管疾病数据处理与可视化分析
这是我写的源码和数据集,百度网盘自取链接
链接:https://pan.baidu.com/s/1JpDA1hipgmm5v8MG9192lg
提取码:yyds
仅供参考学习请不要盗取自用,谢谢
一、赛题背景
心血管疾病 (CVD) 是全球第一大死因,估计每年夺走 1790 万人的生命,占全球所有
死亡人数的 31%。五分之四的心血管疾病死亡是由心脏病发作和中风引起的,其中三分之一
的死亡过早发生在 70 岁以下的人群中。心力衰竭是由 CVD 引起的常见事件,该数据集包
含 11 个可用于分析可能的心脏病的特征。
患有心血管疾病或处于高心血管风险(由于存在一种或多种风险因素,如高血压、糖尿
病、高脂血症或已确诊的疾病)的人需要早期检测和管理。
二、字段说明
Age: 患者年龄
性别:患者的性别[M:男,F:女]
ChestPainType:胸痛类型 [TA:典型心绞痛,ATA:非典型心绞痛,NAP:非心绞痛,ASY:
无症状]
RestingBP:静息血压 [mm Hg]
Cholesterol:血清胆固醇 [mm/dl]
FastingBS:空腹血糖 [1:如果 FastingBS > 120 mg/dl,0:其它]
RestingECG:静息心电图结果 [正常:正常,ST:有 ST-T 波异常(T 波倒置和/或 ST 抬高
或压低 > 0.05 mV),LVH:根据埃斯蒂斯标准显示可能或明确的左心室肥大]
MaxHR:达到的最大心率 [60 到 202 之间的数值]
ExerciseAngina:运动诱发的心绞痛 [Y:是,N:否]
Oldpeak:oldpeak = ST [在抑郁症中测量的数值]
ST_Slope:运动 ST 段的坡度[Up:向上倾斜,Flat:平坦,Down:向下倾斜]
HeartDisease:输出类[1:心脏病,0:正常]
三、任务要求
任务 1 数据预处理
1.1 将 CSV 文件的数据先按照 Age 进行排序,然后取出前 100 条数据,并命名为
“task1_1.csv”保存在结果文件夹中。
1.2 针对取出的前 100 条数据,统计出每个年龄的 HeartDisease 为 1 的条数
1.3 统计出患有心脏病的人群中,各类型 ChestPainType 所占的比例,并写成百分比的格
式以及四舍五入保留两位小数
任务 2 数据可视化
2.1 采用合适的可视化方法展示 HeartDisease 与 Age 之间的关系
2.2 采用合适的可视化方法展示对于 HeartDisease 取不同值,RestingBP 和 Cholesterol 的
大小关系
2.3 采用合适的可视化方法展示 Sex 和 Cholesterol 对 HeartDisease 人数的影响
任务 3 数据分析
3.1 选取适当的方法分析哪些指标的异常会导致心脏病
3.2 针对可能导致心脏病的指标,提出预防心脏病的合理建议
四、任务一
1.1 提取并保存文件“task1_1.csv
首先导入一下数据
df = pd.read_csv('A.csv', encoding='GBK')
常规的观察数据命令
df.head()
df.info()
df.describe()
从结果来看,没有空值。但是从describe()可以发现 MaxHR 都在正常范围内,而 RestingBP、Cholesterol 存在零值,在数值不合理等情况。
df[df['Cholesterol']==0]
df[df['RestingBP']==0]
通过观察表二,我们可以发现 RestingBP(静息血压)、Cholesterol(血清胆固醇)都存在最小值为 0 的情况,而正常情况下静息血压和血清胆固醇并不会为 0,所以我们合理推断该部分数据为缺失值,通过观察原数据发现需要处理的缺失值有172 行数据,为了不对原数据集大小以及特征造成太大损坏,在此我们采用均值填充的方式填补缺失值数据。均值不包括数值为 0 的代处理数据。因为考虑均值填补时,需要根据HeartDisease 进行分类填补,根据 HeartDisease 为 0 和 1 两种情况,求出两类数据的均值分别进行填充
#Cholesterol处理
mm = df[df['HeartDisease']==0]
mm.loc[mm['Cholesterol']==0,'Cholesterol']=mm['Cholesterol'].mean()
nn = df[df['HeartDisease']==1]
nn.loc[nn['Cholesterol']==0,'Cholesterol']=nn['Cholesterol'].mean()
print('mm',mm['Cholesterol'].mean())
print('nn',nn['Cholesterol'].mean())
cc = pd.concat([mm,nn])
cc= cc.reindex(index=range(len(cc)))
cc.shape
df = cc
#RestingBP处理
df.loc[449,'RestingBP'] = nn['RestingBP'].mean()
因为任务中并无明确要求降序排序或是升序排序,在此认为前 100 条数据为降序排序情况下的前 100 条数据。首先对数据文件“A .csv”进行读取,根据列名“Age”进行降序排序,再提取前 100 条数据保存至“task1.csv”文件中,为防止重复运行代码会对该文件进行重复写入,导致任务失败,我们通过利用 os 库对文件是否存在进行判断,存在则不写入,否则创建新文件并写入,代码如下:
df1 = df.sort_values(by='Age',ascending=False)#降序
df1 = df1[:100]
task1 = 'task1.csv'
if os.path.exists(task1):
os.remove(task1)
df1.to_csv('task1.csv', mode='a+', encoding='GBK', header=True, index=0)
“task1.csv”文件以经过数据预处理,0 值已被填充
1.2 针对取出的前 100 条数据,统计出每个年龄的 HeartDisease 为 1 的条数
首先,读取“task1.csv”文件,提取 HeartDisease 为 1 的数据,再经过 groupby()函数对’Age’列进行分组,同时统计各年龄值的数据条数,代码如下:
df2 = pd.read_csv('task1.csv', encoding='GBK')
data = df2[df2['HeartDisease']==1]
data1 = data.groupby('Age').agg({'HeartDisease': 'count'}).sort_values('HeartDisease', ascending=False)
统计的每个年龄的 HeartDisease 为 1 的条数如下:
对该表格数据进行可视化如下:
#可视化代码
# a = data1['HeartDisease'].value_counts()[:]
da_pair = [list(z) for z in zip(data1.index, data1['HeartDisease'])]
print(da_pair)
(
#初始化配置项,内部可设置颜色
Pie(init_opts=opts.InitOpts(bg_color="#fff"))
.add(
#系列名称,即该饼图的名称
series_name="年龄对应HeartDisease为1的条数",
#系列数据项,格式为[(key1,value1),(key2,value2)]
data_pair=da_pair,
#通过半径区分数据大小 “radius” 和 “area” 两种
rosetype="radius",
#饼图的半径,设置成默认百分比,相对于容器高宽中较小的一项的一半
radius="55%",
#饼图的圆心,第一项是相对于容器的宽度,第二项是相对于容器的高度
center=["50%", "50%"],
#标签配置项
label_opts=opts.LabelOpts(is_show=False, position="center"),
)
#全局设置
.set_global_opts(
#设置标题
title_opts=opts.TitleOpts(
#名字
title="年龄对应HeartDisease为1的条数",
#组件距离容器左侧的位置
pos_left="center",
#组件距离容器上方的像素值
pos_top="20",
#设置标题颜色
title_textstyle_opts=opts.TextStyleOpts(color="#2c343c"),
),
#图例配置项,参数 是否显示图里组件
legend_opts=opts.LegendOpts(is_show=False),
)
#系列设置
.set_series_opts(
tooltip_opts=opts.TooltipOpts(
trigger="item", formatter="{a} <br/>{b}: {c} ({d}%)"
),
#设置标签颜色
label_opts=opts.LabelOpts(color="rgba(0, 0, 0, 0.5)"),
)
.render("年龄对应HeartDisease为1的条数.html")
)
可以发现 65 岁、67 岁、69 岁是患有心脏病数量前三的岁数,所以在这个年龄段定期进行心脏病检查,保持良好的生活习惯可以预防心脏病的发生率。
1.3 在患有心脏病的人群中,统计各类型 ChestPainType 所占比例
该任务中,需要对原始数据集提取 HeartDisease 为 1 的数据信息,统计全部HeartDisease 为 1 的数据条数和每个 ChestPainType 类型的数据条数,再经过简单计算即可得出每个类型 ChestPainType 所占比例
#代码
data2 = df[df['HeartDisease']==1]
counts = data2.HeartDisease.sum()
B = data2['ChestPainType'].value_counts()
R_ASY = B[0]/counts
R_NAP = B[1]/counts
R_ATA = B[2]/counts
R_TA = B[3]/counts
print("ASY,NAP,ATA,TA的百分比占比分别是{:.2%},{:.2%},{:.2%},{:.2%}".format(R_ASY,R_NAP,R_ATA,R_TA))
ChestPainType 所占比例表格
五、任务二
2.1 可视化方法展示 HeartDisease 与 Age 之间的关系
在任务一中我们基于 Age 的降序排列的前 100 条数据,简单对 HeartDisease 与 Age 之间的关系进行了可视化,然而需要讨论 HeartDisease 与 Age 的实际关系,还是需要对整体数据进行分析,我们提取出原数据中心脏病患者的行数据,利用 python 以及 pyecharts 库画出每个年龄值心脏病患者人数的条形图,如下:
#可视化代码
bar = Bar().add_xaxis(x_vals)
bar.add_yaxis('心脏病患者数量', y_vals,
markpoint_opts=opts.MarkPointOpts(data=[opts.MarkPointItem(type_='average'),
opts.MarkPointItem(type_='max'),
opts.MarkPointItem(type_='min')],
symbol_size=90)
)
bar.set_series_opts(label_opts=opts.LabelOpts(is_show=True, position='right'))
bar.set_global_opts(title_opts=opts.TitleOpts(title='Age-HeartDisease条形图'))
bar.reversal_axis() #翻转XY轴,将柱状图转换为条形图
bar.render('条形图.html')
根据条形图可以发现最高人数达到 28 人,年龄为 58 岁,平均人数为 11 人,最小人数的有 1 人。心脏病患者的年龄主要集中在[45,65]之间,呈现出中间高,两边低的趋势,说明了年龄大于 45 岁需要更加重视心脏病的预防工作。人体的免疫力和性能大部分会随着年龄的增长逐渐下降,中年人需要承担的责任也会越大,调整压力和情绪,定期做好身体检查,是自身身体健康和家庭幸福的基础保障
2.2 展示对于 HeartDisease 取不同值,RestingBP 和 Cholesterol 的大小关系
由于竞赛数据集中存在 172 个 Cholesterol 数据缺失(取值为 0)的样本,为改善此类缺失值对数据统计的影响,我们对数据提出两种处理方案:第一种是直接剔除数据缺失的样本,第二种是考虑到直接剔除样本会损失大量数据,选择用正常样本的 Cholesterol 均值替换缺失值。其中计算均值时不包括数值为0 的数据。
dff = dff[dff['Cholesterol']!=0]
p2_2 = dff.groupby('HeartDisease').agg({'RestingBP': 'mean','Cholesterol':'mean'})
RestingBP = list(p2_2.values[:,0].round(3))
Cholesterol = list(p2_2.values[:,1].round(3))
from pyecharts.render import make_snapshot
from snapshot_selenium import snapshot
x = ["HeartDisease:0","HeartDisease:1"]
c = (
Bar()
.add_xaxis(x)
.add_yaxis("RestingBP", RestingBP, stack="stack1",category_gap="50%")
.add_yaxis("Cholesterol", Cholesterol, stack="stack1",category_gap="50%")
.set_series_opts(label_opts=opts.LabelOpts(is_show=False))
.set_global_opts(title_opts=opts.TitleOpts(title="RestingBP-Cholesterol大小关系"),
xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=-15),name="因素",name_location = 'center',
name_gap = 35),
yaxis_opts=opts.AxisOpts(name="静息血压:[mm Hg]\n\n血清胆固醇:[mg/dl]",name_location = 'center',
name_gap = 35,),
)
)
c.set_series_opts(label_opts=opts.LabelOpts(is_show=True, position='right'))
c.render('RestingBP-Cholesterol.html')
由图可知,使用两种数据处理方法后的结论近似,患有心脏病的人群中Cholesterol 和 RestingBP 的均值都略高于健康人群。其中患者人群中 Cholesterol平均比健康人群高出 14 ~ 15mg/dl,RestingBP 平均比健康人群高出4~7mm/Hg。可推测 Cholesterol 和 RestingBP 指标偏高的人群患有心脏病的概率更大。
2.3 可视化方法展示 Sex 和 Cholesterol 对 HeartDisease 人数的影响
在我们对心脏病的发病原因进行探究是,不可避免的需要对性别是否会影响心脏病的发病率问题进行分析,在同样的环境条件下,男性或女性需要对心脏病的重视程度是否不同呢?我们基于原数据,求出了 Sex 为 F(女)中心脏病患者的比例,以及 Sex 为 M(男)中心脏病患者的占比,画出水球图对比例进行可视化:Sex 为 F(女) 中心脏病患者的比例水球图:
Sex 为 M(男)中心脏病患者的比例水球图:
根据上面的两个水球图可以发现,男性患有心脏病的几率要比女性患有心脏病的几率高出两倍,其中或许存在生理、物理以及环境的影响,为了进一步确认患有心脏病的概率是否因性别而变化,我们再对心脏病患者中男性的比例进行求解,进行可视化:
all_data2 = df.groupby('Sex').agg({"HeartDisease":'count'})
num_1 = data2.groupby('Sex').agg({"HeartDisease":'count'})
# p2_2 = data2.groupby('ChestPainType').agg({'RestingBP': 'mean','Cholesterol':'mean'})
print(all_data2)
print(num_1)
x1 = num_1.values[0]/all_data2.values[0]
x2 = num_1.values[1]/all_data2.values[1]
x1 = round(float(x1),3)
x2 = round(float(x2),3)
x3 = x2/(x1+x2)
print(x1)
C = None
C = Collector()
#水球图
@C.funcs
def liquid_base() -> Liquid:
c = (
Liquid()
.add("{:.2%}".format(x1), [x1,0.5])
.set_global_opts(title_opts=opts.TitleOpts(title="Sex为F(女)中HeartDisease为1的占比"))
.set_series_opts(label_opts=opts.LabelOpts(is_show=True, position='inside'))
)
return c
@C.funcs
def liquid_base() -> Liquid:
c = (
Liquid()
.add("{:.2%}".format(x2), [x2,0.7])
.set_global_opts(title_opts=opts.TitleOpts(title="Sex为M(男)中HeartDisease为1的占比"))
.set_series_opts(label_opts=opts.LabelOpts(is_show=True, position='inside',color= '#FF6633'))
)
return c
# c.set_series_opts(label_opts=opts.LabelOpts(is_show=True, position='right'))
@C.funcs
def liquid_base() -> Liquid:
c = (
Liquid()
.add("{:.2%}".format(x3), [x3,0.7])
.set_global_opts(title_opts=opts.TitleOpts(title="HeartDisease为1中Sex为M(男)的占比"))
.set_series_opts(label_opts=opts.LabelOpts(is_show=True, position='inside',color= '#FF6633'))
)
return c
Page().add(*[fn() for fn, _ in C.charts]).render(path='水球图.html')
可以发现男性无可避免地成为了心脏病患者中的主体,说明了性别确实是影响心脏病发病几率的因素之一,男性需要比女性更加注意身体的变化情况,学会平衡生活和工作的压力,增加日常运动量,保持健康的生理、心理情况。另外,我们将 Age 作为横坐标,Cholesterol 作为纵坐标,画出气泡图分析Cholesterol 对 HeartDisease 的影响:
Not_Heart1_data3 = df[df['HeartDisease']==0]
data_pair1 = [list(z) for z in zip(Not_Heart1_data3['Age'], Not_Heart1_data3['Cholesterol'])]
x_vals1 = []
y_vals1 = []
for i in range(len(data_pair1)):
x_vals1.append(data_pair1[i][0])
y_vals1.append(int(data_pair1[i][1]))
data_pair2 = [list(z) for z in zip(data2['Age'], data2['Cholesterol'])]
x_vals2 = []
y_vals2 = []
for i in range(len(data_pair2)):
x_vals2.append(data_pair2[i][0])
y_vals2.append(int(data_pair2[i][1]))
y_200 = 0
y_400 = 0
y_600 = 0
y_800 = 0
for i in range(len(data_pair1)):
if int(data_pair1[i][1])<=150:
y_200 = y_200 + 1
elif int(data_pair1[i][1])<=300:
y_400 = y_400 + 1
elif int(data_pair1[i][1])<=450:
y_600 = y_600 + 1
elif int(data_pair1[i][1])>450:
y_800 = y_800 + 1
a1 = 0
a2 = 0
a3 = 0
a4 = 0
for i in range(len(data_pair2)):
if int(data_pair2[i][1])<=150:
a1 = a1 + 1
elif int(data_pair2[i][1])<=300:
a2 = a2 + 1
elif int(data_pair2[i][1])<=450:
a3 = a3 + 1
elif int(data_pair2[i][1])>450:
a4 = a4 + 1
p1 = round(a1/y_200,3)
p2 = round(a2/y_400,3)
p3 = round(a3/y_600,3)
p4 = round(a4/y_800,3)
p = [p1,p2,p3,p4]
from pyecharts import options as opts
from pyecharts.charts import Scatter
from pyecharts.globals import ThemeType
from pyecharts.commons.utils import JsCode
scatter = (Scatter()
.add_xaxis(x_vals1)
.add_yaxis("健康人", y_vals1,
# 渐变效果实现部分
color=JsCode("""new echarts.graphic.RadialGradient(0.4, 0.3, 1, [{
offset: 0,
color: 'rgb(251, 118, 123)'
}, {
offset: 1,
color: 'rgb(204, 46, 72)'
}])"""))
.add_xaxis(x_vals2)
.add_yaxis("心脏病患者", y_vals2,
# 渐变效果实现部分
color=JsCode("""new echarts.graphic.RadialGradient(0.4, 0.3, 1, [{
offset: 0,
color: 'rgb(129, 227, 238)'
}, {
offset: 1,
color: 'rgb(25, 183, 207)'
}])"""))
.set_series_opts(label_opts=opts.LabelOpts(is_show=False))
.set_global_opts(
title_opts=opts.TitleOpts(title="Cholesterol与HeartDisease的关系"),
tooltip_opts = opts.TooltipOpts(
formatter=JsCode("function (param) {return param.data[2];}")),
xaxis_opts=opts.AxisOpts(
# 设置坐标轴为数值类型
type_="value",
name="Age",name_location = 'center',
name_gap = 35,
# 显示分割线
splitline_opts=opts.SplitLineOpts(is_show=True)),
yaxis_opts=opts.AxisOpts(
# 设置坐标轴为数值类型
type_="value",
# 默认为False表示起始为0
name="Cholesterol",name_location = 'center',
is_scale=True,
name_gap = 35,
splitline_opts=opts.SplitLineOpts(is_show=True),),
# 数据中第三个度量值通过图形的size来展示
visualmap_opts=opts.VisualMapOpts(is_show=True, type_='size', min_=df['Cholesterol'].min(), max_=df['Cholesterol'].max())
))
scatter.render('气泡图.html')
scatter.render_notebook()
根据气泡图观察,数据集中 Age 主要集中在 20-80 区间内,整体观察可得红色气泡(心脏病患者)的 Cholesterol 大小略微比蓝色气泡(健康人)要高一些,不过气泡图的分析效果比较粗糙,我们进一步可视化对该问题进行分析:
横坐标以 150 为间隔,分析了每个 Cholesterol 区间内,患者与健康人的人数比值(患者人数/健康人人数),在区间 Cholesterol<=150 时,比值为 1,在150<Cholesterol<=300 时,比值为 1.259,在区间 300<Cholesterol<=450 时,比值为 1.111,在区间 Cholesterol>450 时,比值为 1.667,该比值一直在 1 及以上,并不是说明人群中心脏病患者人数比例接近 0.5,这是由于数据集的影响,并不能代表心脏病的患者比率,但是我们通过分析其比值的变化来分析 Cholesterol 是否能影响心脏病的发病情况。折线图中折线整体为上升趋势,并且比值的均值为1.26,最大值为 1.67,说明了随着 Cholesterol 的增大,心脏病患者的人数会有小幅度的上升,即 Cholesterol 也是心脏病的诱发因素之一。
六、任务三
3.1.1 选取适当的方法分析哪些指标的异常会导致心脏病
心脏病的发生往往是难以预测的,我们可以通过定期进行相关指标的检查来判断是否会引发心脏病,准确、及时的预防工作往往要比治疗过程更加安全、可靠。因此分析出哪些指标的异常会导致心脏病就尤其重要。是否患有心脏病,这是一个二分类任务,而在分类任务中,决策树的分类效果是出类拔萃的,因此本文采用了决策树来分析哪些指标的异常会导致心脏病。因为数据中存在定量特征和定性特征,需要对定性特征进行相关处理,以保证决策树的合理性。
##################找出所有的非数值列(object列),生成新的子集,以便后续进行处理#############
#判断各列数据类型是否为object
mask = df.dtypes == np.object
#将为object的列的列名保存到categorical_cols
categorical_cols = df.columns[mask]
data_obj=df[categorical_cols]
data_obj.head()
我们采用独热编码的方式对定性特征进行转化,再与定量数据结合成为新的数据集。示例图如下:
实际转化情况如下:
num_ohc_cols = (df[categorical_cols].apply(lambda x: x.nunique()))
num_ohc_cols=num_ohc_cols.sort_values(ascending=False)
num_ohc_cols
from sklearn.preprocessing import OneHotEncoder, LabelEncoder
# 创建编码器实例
le = LabelEncoder()
ohc = OneHotEncoder()
data_encoded = df
for col in num_ohc_cols.index:
# 先使用LabelEncoder实现字符串编码
object_dat = le.fit_transform(data_encoded[col])
print(le.classes_)
print(le.fit(object_dat).classes_)
# 再使用OneHotEncoder实现独热编码
new_object_dat = ohc.fit_transform(object_dat.reshape(-1,1))
# print(new_object_dat)
# 为new_dat创建新的列名,新列名为“原列名_列索引”
n_cols = new_object_dat.shape[1]
col_names = ['_'.join([col, str(x)]) for x in range(n_cols)]
# print(col_names)
#用新列名和原行索引建立新的DataFrame
new_object_df=pd.DataFrame(new_object_dat.toarray(),columns=col_names,index=data_encoded.index)
# 从数据中删除原始列
data_encoded = data_encoded.drop(col, axis=1)
# 将新数据追加到 dataframe
data_encoded = pd.concat([data_encoded, new_object_df], axis=1)
data_encoded.head()
# 将SalePrice列以外的列都作为特征集
feature_cols_encoded = [x for x in data_encoded.columns if x not in 'HeartDisease']
feature_encoded = data_encoded[feature_cols_encoded]
print('特征集维数 ',feature_encoded.shape)
# 将SalePrice列作为特征集
label_cols_encoded = 'HeartDisease'
label_encoded = data_encoded[label_cols_encoded]
print('标签集维数 ',label_encoded.shape)
Xtrain_encoded,Xtest_encoded,ytrain_encoded,ytest_encoded=train_test_split(feature_encoded,label_encoded,test_size=0.3)
决策树的相关设置为:分类指标为熵值,因为论文篇幅限制,权衡决策树的分类效果,树的深度设置为 3,最小样本分割设置为 80。
dt = tree.DecisionTreeClassifier(criterion='entropy', max_depth=3,
random_state=1,splitter='random', min_samples_split=80)
dt = dt.fit(Xtrain_encoded, ytrain_encoded)
score=dt.score(Xtest_encoded,ytest_encoded)
score
利用 sklearn 库创建决策树代码得到分类效果如下为训练集上的正确率为:0.8489096573208723,测试集上的正确率为:0.8333333333333334,再根据预测
结果求出 F1、召回率以及准确率等指标:
得到的决策树进行可视化:
import graphviz
from io import StringIO
import pydotplus
from ipywidgets import Image
# feature_name=['Age','Sex','ChestPainType','RestingBP','Cholesterol','FastingBS','RestingECG','MaxHR','ExerciseAngina','Oldpeak','ST_Slope']
dot_data = StringIO()
tree.export_graphviz(dt, out_file=dot_data,
feature_names=feature_cols_encoded,
class_names=['Health','patient'],
filled=True,rounded=True)
graph = pydotplus.graph_from_dot_data(dot_data.getvalue())
Image(value=graph.create_png())
# graph=graphviz.Source(dot_data)
# graph
对决策树进行美化,其中绿色为患者,黄色为健康:
from dtreeviz.trees import dtreeviz
viz = dtreeviz(dt,
feature_encoded,
label_encoded,
target_name='',
feature_names=feature_cols_encoded,
class_names={0:'Health',1:'Patient'},scale=2)
viz.view()
根据美化后的决策树以及编码转化表分析,可以发现父节点中,ST_Slope_2的熵值是最大的,而其分类的的依据是运动 ST 段的坡度是否为 Up:向上倾斜,若是,则分到 Health 类,再基于此点计算每个指标的熵值,得到 ChestPainType_0指标为熵值的最大值,再分类。我们可以从整体来观察每一个分类的指标,包括了 ST_Slope、ChestPainType、MaxHR、Sex、Age、ExerciseAngina 共 6 个指标,在对决策树进行建立的时候,这 6 个指标起到比较重要的作用,通过检查这 6 个指标是否异常可以很好的检测到是否会导致心脏病。接着我们进一步对年龄和最大心率进行可视化分析
health = df[df['HeartDisease']==0]
patient = df[df['HeartDisease']==1]
health_pair = [list(z) for z in zip(health['Age'], health['MaxHR'])]
patient_pair = [list(z) for z in zip(patient['Age'], patient['MaxHR'])]
# print(len(data_pair1))
# print(data_pair)
x_hea = []
y_hea = []
x_pat = []
y_pat = []
for i in range(len(health_pair)):
x_hea.append(health_pair[i][0])
y_hea.append(int(health_pair[i][1]))
for i in range(len(patient_pair)):
x_pat.append(patient_pair[i][0])
y_pat.append(int(patient_pair[i][1]))
scatter = Scatter()
scatter.add_xaxis(health['Age'])
scatter.add_yaxis("Health", y_hea,symbol_size=8)
scatter.add_xaxis(patient['Age'])
scatter.add_yaxis("Patient", y_pat,symbol_size=8)
# scatter.set_global_opts(title_opts=opts.TitleOpts(title="Scatter-基本示例"))
scatter.set_series_opts(label_opts=opts.LabelOpts(is_show=False))
scatter.set_global_opts(
title_opts=opts.TitleOpts(title="Age and Maximum Heart Rate"),
xaxis_opts=opts.AxisOpts(splitline_opts=opts.SplitLineOpts(is_show=False),name="Age",name_location = 'center',
name_gap = 35,),
yaxis_opts=opts.AxisOpts(splitline_opts=opts.SplitLineOpts(is_show=False),name="MaxHR",name_location = 'center',
name_gap = 35,),
)
scatter.render('年龄和最大心率.html')
散点图中,红色的点代表健康人,黑色的点代表患者,可以发现年老的人患有心脏病的概率较高,并且随着年龄的增长,最大心率会有所下降。另外,最大心率高的人群患有心脏病的几率也高,不局限于年龄。最后,我们分析 RestingECG 的不同类型对心脏病的影响,建立柱形图并且添加折线反映健康人与患者人数的数值变化。
from pyecharts.render import make_snapshot
from snapshot_selenium import snapshot
p2_2 = df.groupby('HeartDisease').agg({})
Normal = df[df['RestingECG']=='Normal']
ST = df[df['RestingECG']=='ST']
LVH = df[df['RestingECG']=='LVH']
# print(type(Normal['HeartDisease'].value_counts()))
y1 = Normal['HeartDisease'].value_counts()
y2 = ST['HeartDisease'].value_counts()
y3 = LVH['HeartDisease'].value_counts()
print(y3)
y1 = list(y1.values)
y2 = list(y2.values)
y3 = list(y3.values)
# y1 = list(y1.values[:,0].round(3))
# Cholesterol = list(p2_2.values[:,1].round(3))
print(y1)
x = ["HeartDisease:1","HeartDisease:0"]
def overlap_line_scatter():
bar = (Bar()
.add_xaxis(x)
.add_yaxis("Normal", [285, 267])
.add_yaxis("ST", [117, 61])
.add_yaxis("LVH", [106, 82])
.set_series_opts(label_opts=opts.LabelOpts(is_show=False))
.set_global_opts(title_opts=opts.TitleOpts(title="RestingECG结果对心脏病的影响"),
xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=-15),name_location = 'center',
name_gap = 35),
yaxis_opts=opts.AxisOpts(name="人数",name_location = 'center',
name_gap = 35,),
)
)
line = (
Line()
.add_xaxis(x)
.add_yaxis("Normal", [285, 267])
.add_yaxis("ST", [117, 61])
.add_yaxis("LVH", [106, 82])
)
bar.overlap(line)
return bar
overlap_line_scatter().render('RestingECG结果对心脏病的影响.html')
由图观察可得,患者数量在 ST、LVH 两种类型中由明确的上升,而 Normal类型中上升不是很明显,说明静息心电图结果中,有 ST-T 波异常或埃斯蒂斯标准显示可能或明确的左心室肥大都会有较高的几率是心脏病患者,应该提高重视。
3.1.2 针对可能导致心脏病的指标,提出预防心脏病的合理建议
任务二中我们发现了心脏病患者的 Age 主要集中在[45,65]之间,呈现出中间高,两边低的趋势。在 RestingBP 和 Cholesterol 的可视化分析中可得,患者人群中 Cholesterol 平均比健康人群高出 14 ~ 15mg/dl,RestingBP 平均比健康人群高出 4~7mm/Hg。另外男性患有心脏病的几率要比女性患有心脏病的几率高出两倍。根据折线图可以发现随着 Cholesterol 的增大,心脏病患者的人数会有小幅度的上升,其中心脏病患者人数与健康人人数的比值最高可达到 1.67,平均比值为 1.26。
放一小段,后面省略了,自我发挥。。。