前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >贝叶斯实例中风预测详解--python

贝叶斯实例中风预测详解--python

作者头像
司六米希
发布2022-11-15 18:58:52
9070
发布2022-11-15 18:58:52
举报
文章被收录于专栏:司六米希司六米希

贝叶斯中风预测详解--python

1. 内容描述

中风预测:根据世界卫生组织(WHO)的数据,中风是全球第二大死亡原因,约占总死亡人数的11%。该数据集用于根据输入参数(例如性别,年龄,各种疾病和吸烟状况)预测患者是否可能中风。数据中的每一行都提供有关患者的相关信息。 https://mp.weixin.qq.com/s/QobTa9eN0snb9u2lXxX_iQ 70%训练贝叶斯模型,30%预测 数据集👇 链接:https://pan.baidu.com/s/1e47HLe-icM56cTmlWHppQA 提取码:wfey

1.1 字段描述

在这里插入图片描述
在这里插入图片描述

1.2 Exploratory Data Analysis探索性数据分析

1.2.1数据整体信息以及统计特征

代码语言:javascript
复制
import pandas as pd
data = pd.read_csv('strokePredictionData.csv')
## 打印数据基本信息
print(data.info())
## 打印数据的统计特征
print(data.describe())

结果👇

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

统计值变量说明: count:数量统计,此列共有多少有效值 unipue:不同的值有多少个 std:标准差 min:最小值 25%:四分之一分位数 50%:二分之一分位数 75%:四分之三分位数 max:最大值 mean:均值

1.2.2 id

id属性是用于分配给每个患者的唯一编号进行跟踪使用,对此于模型使用过程中无用,可进行删除操作

代码👇

代码语言:javascript
复制
# 删除id列
data.drop("id", inplace=True, axis=1)
drop函数(删除操作)说明:
drop函数的使用:删除行、删除列。drop函数默认删除行,列需要加axis = 1【注意:凡是会对原数组作出修改并返回一个新数组的,往往都有一个 inplace可选参数。如果手动设定为True(默认为False),那么原数组直接就被替换。也就是说,采用inplace=True之后,原数组名对应的内存值直接改变】
# 查询表头看是否还有id,仅是验证查看
print(data.head(0))

结果👇(已无id列)

在这里插入图片描述
在这里插入图片描述

1.2.3 gender性别

性别属性说明患者的性别,对此进行中风率和性别对比。由于性别属性有三种值(Male、Female gender、Other),对此采用计数柱状图来进行比较。

代码👇

代码语言:javascript
复制
# 为方便对比,创建一个1行2列的画布,figsize设置画布大小
fig, axes = plt.subplots(1, 2, figsize=(10, 5),)
# 提供关于它的唯一值以及每个值的计数的信息
print('计数 \n', data['gender'].value_counts())
#设置画板颜色风格,I am Purple lover
sns.set_palette("magma")
# 计数柱状图绘制
sns.countplot(data=data, x='gender',ax=axes[0])
sns.countplot(data=data, x='gender', hue='stroke',ax=axes[1])
plt.show()

结果👇

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

结果分析👇 虽然男女性别数据集并不完全平衡。但由此可见,不同性别之间的中风率没有太大的区别

1.2.4 age年龄

针对年龄属性进行分布图以及分箱图绘制 代码👇

代码语言:javascript
复制
data['age'].nunique()
sns.displot(data['age'])
plt.figure(figsize=(15, 7))
sns.boxplot(data=data, x='stroke', y='age')

结果👇

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

结果分析👇 60岁以上的人更容易患中风

1.2.5 Hypertension高血压

高血压在老年人中对比年轻人很常见,高血压会可能导致中风

代码👇

代码语言:javascript
复制
data['hypertension'].nunique()
sns.countplot(data=data, x='hypertension', hue='stroke',ax=axes[1])
plt.show()

结果👇

在这里插入图片描述
在这里插入图片描述

结果分析👇 高血压人群更容易患中风

1.2.6 heart_disease心脏病

在这里插入图片描述
在这里插入图片描述

1.2.7 ever_married已婚与否

代码👇

代码语言:javascript
复制
data['ever_married'].nunique()
sns.countplot(data=data, x='ever_married', hue='stroke')
plt.show()

结果👇

在这里插入图片描述
在这里插入图片描述

结果分析👇 已婚人士的中风率更高

1.2.8 work_type工作类型

代码👇

代码语言:javascript
复制
sns.countplot(data=data, x='work_type')
sns.countplot(data=data, x='work_type', hue='stroke')
plt.show()

结果👇

在这里插入图片描述
在这里插入图片描述

结果分析👇 在私营部门工作的人2号患中风的风险更高。从未工作过的人4号中风率非常低

1.2.9 Residence_type居住类型

代码👇

代码语言:javascript
复制
sns.countplot(data=data,x='Residence_type')
plt.show()

结果👇

在这里插入图片描述
在这里插入图片描述

结果分析👇 对中风患者影响差异不大

1.2.10 avg_glucose_level患者体内的平均血糖水平

代码👇

代码语言:javascript
复制
data['avg_glucose_level'].nunique()
sns.displot(data['avg_glucose_level'])
sns.boxplot(data=data, x='stroke', y='avg_glucose_level')
plt.show()

结果👇

在这里插入图片描述
在这里插入图片描述

结果分析👇 中风患者的平均血糖水平偏高

1.2.11 bmi

代码👇

代码语言:javascript
复制
sns.countplot(data=data,x='bmi')
sns.boxplot(data=data,x='stroke',y='bmi')
plt.show()

结果👇

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

结果分析👇 BMI针对中风几率影响不大

1.2.12 smoking_status吸烟状况

根据计数图,吸烟状况人数分布以及中风情况 代码👇

代码语言:javascript
复制
sns.countplot(data=data,x='smoking_status',hue='stroke')
plt.show()

结果👇

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

结果分析👇 无论吸烟状况如何,中风的几率都没有太大差异

1.3 特征工程

1.3.1 标签编码

由于数据集由分类数据和数值数据组成,对此使用标签编码器(将分类数据转换为数字数据0——(n-1))将分类数据编码为数值数据。 代码👇

代码语言:javascript
复制
# 获取数据类型为object的列
cols = data.select_dtypes(include=['object']).columns
# 打印出object的列检查
print(cols)
# 标签编码初始化
le = LabelEncoder()
# 将分类数据转换为数字
data[cols] = data[cols].apply(le.fit_transform)
# 随机找个object的列进行检查,看是否已将分类数据编码为数值数据
print(data.head(10).work_type)

结果👇

在这里插入图片描述
在这里插入图片描述

1.3.2 特征相关性检查

通过1.3.1EDA进行初步数据分析,对此采用热图以及 SelectKBest 和 F_Classif 进一步检查特征

1.3.2.1 热图
代码语言:javascript
复制
代码👇
代码语言:javascript
复制
# 创建15*10的画布
plt.figure(figsize=(15,10))
print(data.corr())
# data.corr()函数说明
# data.corr()表示了data中的两个变量之间的相关性,取值范围为[-1,1],取值接近-1,表示反相关,类似反比例函数,取值接近1,表正相关
sns.heatmap(data.corr(),annot=True,fmt='.2')
# 参数说明
# data:数据data中的两个变量之间的相关性
# annot:
# annotate的缩写,annot默认为False,当annot为True时,在heatmap中每个方格写入数据
# annot_kws,当annot为True时,可设置各个参数,包括大小,颜色,加粗,斜体字等
# fmt: 格式设置
plt.show()
代码语言:javascript
复制
结果👇
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
1.3.2.2 SelectKBest and F_Classif

使用sklearn中的feature_selection库中SelectKBest函数进行特征选择,参数中的score_func选择来进行特征选择F检验(f_classif)【计算样本的方差分析f值】 代码以及函数解析👇

代码语言:javascript
复制
#处理数据空值,用0代替
data  = data.replace(np.nan, 0)
# 特征选择F检验(f_classif)
#参数说明:score_func[得分方法]
classifiers = SelectKBest(score_func=f_classif, k=5)
# 用于计算训练数据的均值和方差
fits = classifiers.fit(data.drop('stroke', axis=1), data['stroke'])
# DataFrame的单元格可以存放数值、字符串等,这和excel表很像,同时DataFrame可以设置列名columns与行名index
x = pd.DataFrame(fits.scores_)
print(x)
columns = pd.DataFrame(data.drop('stroke', axis=1).columns)
# concat函数是pandas底下的方法,可以把数据根据不同的轴进行简单的融合
# pd.concat(objs, axis=0, join='outer', join_axes=None, ignore_index=False,
#        keys=None, levels=None, names=None, verify_integrity=False)
# 参数说明:
# objs:series,dataframe,或者panel构成的序列list
# axis:0 行,1列
fscores = pd.concat([columns, x], axis=1)
fscores.columns = ['属性特征', '得分']
# sort_values()是pandas中比较常用的排序方法,其主要涉及以下三个参数:
# by : str or list of str(字符或者字符列表)
# Name or list of names to sort by.
# 当需要按照多个列排序时,可使用列表
# ascending : bool or list of bool, default True
# (是否升序排序,默认为true,降序则为false。如果是列表,则需和by指定的列表数量相同,指明每一列的排序方式)
fscores.sort_values(by='得分', ascending=False)
plt.show()
print(fscores)

结果👇

在这里插入图片描述
在这里插入图片描述

经过EDA,热图以及SelectKBest 和 F_Classif 特征检查,最终特征筛选为age(年龄)、hypertension(高血压)、heart_disease(心脏病)、ever_married(是否已婚)、avg_glucose_level(平均血糖水平)

注意:筛选特征和特征降维不同 本文仅是筛选特征,特征降维大伙可以自己试试,特征降维会考虑特征之间的相互关系,从而产生新特征来成为训练集指标。

1.3.3 连续型数据处理

代码👇

代码语言:javascript
复制
#针对葡萄糖水平进行分箱处理
data.avg_glucose_level = pd.cut(data.avg_glucose_level,4,labels=[0,1,2,3])    # 实现等距分箱,分为4个箱,并用0,1,2,3替代原数据
print(data.avg_glucose_level)
#针对年龄进行分箱处理
data.age = pd.cut(data.age,4,labels=[0,1,2,3])    # 实现等距分箱,分为4个箱
print(data.age)

结果👇

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

分箱区间👇

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.4 贝叶斯模型描述

贝叶斯公式 设实验E为样本空间,A为E的事件,B1,B2,…,Bn为Ω的一 个分割,且P(Bi)>0,i=1,2,…,n,则由:

在这里插入图片描述
在这里插入图片描述

上式被称为贝叶斯公式

1.5 数据集拆分

根据题目要求70%训练贝叶斯模型,30%预测 (即训练集3577,测试集1533) 代码👇

代码语言:javascript
复制
# 分割数据
train_x, test_x, train_y, test_y = train_test_split(data,data['stroke'], random_state=1, test_size=0.3)
# train_test_split 函数【从 sklearn.model_selection 中调用】参数说明
# train_data:所要划分的样本特征集
# train_target:所要划分的样本结果
# test_size:样本占比,如果是整数的话就是样本的数量
# random_state:是随机数的种子。
# 随机数种子:是该组随机数的编号,在需要重复试验的时候,保证得到一组一样的随机数。
# 数据形式👇
print(train_x.shape,  train_y.shape, test_y.shape,test_x.shape)

结果👇

在这里插入图片描述
在这里插入图片描述

将筛选后的特征部分经过标签编码处理后的数据进行数组化处理,并将筛选特征age(年龄)、hypertension(高血压)、heart_disease(心脏病)、ever_married(是否已婚)、avg_glucose_level(平均血糖水平)以及中风与否构成need_data数据集 代码👇

代码语言:javascript
复制
#数组化处理
data_age=Series.tolist(train_x.age)
data_hypertension=Series.tolist(train_x.hypertension)
data_heart_disease=Series.tolist(train_x.heart_disease)
data_ever_married=Series.tolist(train_x.ever_married)
data_avg_glucose_level=Series.tolist(train_x.avg_glucose_level)
data_stroke= Series.tolist(train_y)
#np.vstack拼接数组
need_data=np.vstack((data_age,data_hypertension,data_heart_disease,data_ever_married,data_avg_glucose_level,data_stroke)).tolist()
#检验查看处理结果
print(need_data)

结果👇

在这里插入图片描述
在这里插入图片描述

1.6 模型创建

根据EDA、热图、SelectKBest 和 F_Classif综合分析降维后的age、hypertension、heart_disease、ever_married、avg_glucose_level五种特征,高年龄、已婚、 高血压、有心脏病、 平均血糖水平高者,中风概率高。对此根据贝叶斯原理进行题目贝叶斯公式推得 P(中风|高年龄已婚高血压有心脏病平均血糖高) =P(高年龄已婚高血压有心脏病平均血糖高|中风)P(中风) /P(高年龄已婚高血压有心脏病*平均血糖高)

1.6.1 先验条件计算

先验概率P(Bi)(i=1,2,…)表示各种原因发生的可能性大小 代码👇

代码语言:javascript
复制
def train_1(self):
 # 统计data_stroke的种类及数量,用于后续计算
    count_y = Counter(self.t_data[5])
    print(count_y)
    # 先统计y的种类,并计算P(Y=c)的先验概率,再切分训练数据
    # 计算先验概率并对应y值存入字典,然后根据不同的y切分数据,各自存入一个列表,这些列表存于字典ys
    # 统计y的种类,并计算概率,再切分训练数据
    ys = {}
    for y in count_y.keys():
        # print(count_y.keys())
        # dict_keys([0.0, 1.0])
        ys[y] = []
    # 计算先验概率并对应y值存入字典
        self.p_y[y] = count_y[y] / len(self.t_data[0])
        # print(count_y[y])# 结果为3411,166
        # print(self.p_y[y] )#先验概率结果0.9535923958624546 以及 0.04640760413754543

    # 遍历数据,根据其y存入对应列表
    for i in range(len(self.t_data[0])):
        # 将数据切分后分别存入字典中的列表,key是对应的y值
        # print(self.t_data[:, i]) #eg;[47.   0.   0.   1.  72.2  0. ]每个个体数据
        # print(self.t_data[5][i])#中风与否
        ys[self.t_data[5][i]].append(self.t_data[:, i])#将对应的中风数组与0.0,未中风数组与1.0形成字典
    print('完成数据处理,我要开始学习了')
    for item in ys.items():
        # print(ys.items())# items() 以列表返回可遍历的(键, 值) 元组数组
        # print(item)
        # print('hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh/n')
        self.train_2(item)
    print('学习完毕!可以开始预测')

def train_2(self, _y):
    # 先把数据转化为矩阵,便于接下来切片统计运算
    print(_y)
    data = np.array(_y[1])
    # print(_y[1])
    # 计算P(Xi=k | Y = 中风与否)的先验概率,统计每个特征的值的种类
    count_x1 = Counter(data[:, 0])
    count_x2 = Counter(data[:, 1])
    count_x3 = Counter(data[:, 2])
    count_x4 = Counter(data[:, 3])
    count_x5 = Counter(data[:, 4])
    # 检查结果
    #正是出现两部分count_x[0-5]中风与否的两种先验概率
    # print('count_x1',count_x1)
    # print('count_x2',count_x2)
    # print('count_x3',count_x3)
    # print('count_x4', count_x4)
    # print('count_x5', count_x5)
    # 计算相应的概率,存入字典
    for x1 in count_x1.keys():
        self.p_x1_y['{}_{}'.format(x1, _y[0])] = count_x1[x1] / len(data)
    for x2 in count_x2.keys():
        self.p_x2_y['{}_{}'.format(x2, _y[0])] = count_x2[x2] / len(data)
    for x3 in count_x3.keys():
        self.p_x3_y['{}_{}'.format(x3, _y[0])] = count_x3[x3] / len(data)
    for x4 in count_x4.keys():
        self.p_x4_y['{}_{}'.format(x4, _y[0])] = count_x4[x4] / len(data)
    for x5 in count_x5.keys():
        self.p_x5_y['{}_{}'.format(x5, _y[0])] = count_x5[x5] / len(data)

1.6.2 后验条件计算(单组)

后验概率: P(Bi|A)(i=1,2…)则反映当产生了中风结果A之后,再对各种原因概率的新认识,故称,在此采用输入方式进行检验查看数据预测状况

代码👇

代码语言:javascript
复制
def analyse_input(self):  # 计算后验概率并比较
in_datas = input('输入x1,x2,x3,x4,x5(空格隔开):').split(' ')
    p_p = 0
    result = []
    #将输入类型str转换至与x1,x2,3,x4,5
    in_data=[1,2,3,4,5]
    in_data[0]=int(in_datas[0])
    in_data[1] = int(in_datas[1])
    in_data[2] = int(in_datas[2])
    in_data[3] = int(in_datas[3])
    in_data[4] = int(in_datas[4])
    # print(type(in_data[4]))
    for j in self.p_y.keys():
      # try:
        pp = self.p_y[j] * self.p_x1_y['{}_{}'.format(in_data[0], j)] *self.p_x2_y['{}_{}'.format(in_data[1], j)]* self.p_x3_y['{}_{}'.format(in_data[2], j)]*self.p_x4_y['{}_{}'.format(in_data[3], j)] *self.p_x5_y['{}_{}'.format(in_data[4], j)]
        # print(self.p_y[j])
        if self.p_y[j]>0.5:
            print('未中风概率为',pp)
        else:
            print('中风概率为',pp)
        if pp >= p_p:  # 观察到,对于相同的输入,可能出现两种不同预测结果(对于本次数据来说,只有两种结果),要对此做处理
            if pp > p_p:  # 若有出现更大的概率,需要把先前已有的所有结果全部替换
                if not result:  # 开始的时候列表是空的,如果直写循环替换,其实那个循环根本不会开始。如果在循环后添加,那将会导致接下来有的结果会重复进入列表(被替换的和被添加的)
                    result.append(j)
                else:
                    for r in range(len(result)):
                        result[r] = j
            elif p_p == pp:
                result.append(j)
            p_p = pp
      # except:
      #   print(result)
    result = list(set(result))
    if len(result) == 1:
        print('预测结果为:{}'.format(result[0]))
    else:
        print('可能结果如下:', end='')
        for e in result:
            print(e)

结果👇

在这里插入图片描述
在这里插入图片描述

1.6.3 后验条件计算(测试集)

代码👇

代码语言:javascript
复制
def analyse_input2(self):  # 计算测试集数据后验概率并比较
	p_p = 0
    result = []
    in_data=[1,2,3,4,5]
    # print(self.c_data.shape[1])
    for m in range(0,self.c_data.shape[1]):
        #将输入类型str转换至与x1,x2,x3,x4,x5
        in_data[0]=int(self.c_data[0,m])
        in_data[1] = int(self.c_data[1,m])
        in_data[2] = int(self.c_data[2,m])
        in_data[3] = int(self.c_data[3,m])
        in_data[4] = int(self.c_data[4,m])
        # print(type(in_data[4]))
        for j in self.p_y.keys():
          # try:
            pp = self.p_y[j] * self.p_x1_y['{}_{}'.format(in_data[0], j)] *self.p_x2_y['{}_{}'.format(in_data[1], j)]* self.p_x3_y['{}_{}'.format(in_data[2], j)]*self.p_x4_y['{}_{}'.format(in_data[3], j)] *self.p_x5_y['{}_{}'.format(in_data[4], j)]
            # print(self.p_y[j])
            if self.p_y[j]>0.5:
                print('未中风概率为',pp)
            else:
                print('中风概率为',pp)
            if pp >= p_p:  # 观察到,对于相同的输入,可能出现两种不同预测结果(对于本次数据来说,只有两种结果),要对此做处理
                if pp > p_p:  # 若有出现更大的概率,需要把先前已有的所有结果全部替换
                    if not result:  # 开始的时候列表是空的,如果直写循环替换,其实那个循环根本不会开始。如果在循环后添加,那将会导致接下来有的结果会重复进入列表(被替换的和被添加的)
                        result.append(j)
                    else:
                        for r in range(len(result)):
                            result[r] = j
                elif p_p == pp:
                    result.append(j)
                p_p = pp
          # except:
          #   print(result)
        result = list(set(result))
        # print('hhhhhresult',result)
        if len(result) == 1:
            print('预测结果为:{}'.format(result[0]))
            self.predict[m]=result[0]
            # print(m)#0-1532
        else:
            print('可能结果如下:', end='')
            for e in result:
                print(e)

结果👇

在这里插入图片描述
在这里插入图片描述

1.7 结果评估

1.7.1准确率计算

代码语言:javascript
复制
         准确率(Precision) =  系统检索到的相关事件 / 系统所有检索到的事件总数
代码语言:javascript
复制
#计算得分
		def score(self, test_target):
            count=0
            # print(np.array(test_target))
            # print(np.array(self.predict.values()))
            # 数据格式转换
            T=str(list(self.predict.values()))
            s=str(list(np.array(test_target)))
            # print(type(n),type(s))
            for i in range(0, test_target.shape[0]):
                print(i)
                print(s[i],T[i])
                if s[i] == T[i]:
                    count += 1# 累计正确数
            score = count / (test_target.shape[0])
            print('the accuracy is:', score)

结果👇

在这里插入图片描述
在这里插入图片描述

1.7.2 召回率计算

召回率(Recall) = 系统检索到的相关事件 / 系统所有相关的事件总数 代码👇

代码语言:javascript
复制
#计算得分
def score(self, test_target):
            count=0
            number=0
            # print(np.array(test_target))
            # print(np.array(self.predict.values()))
            # 数据格式转换
            T=str(list(self.predict.values()))
            s=str(list(np.array(test_target)))
            # print(type(n),type(s))
            for i in range(0, test_target.shape[0]):
                print(i)
                print(s[i],T[i])
                if s[i] == T[i]:
                    count += 1
                if s[i] !=T[i]:
                    number+=1
            score = count / (test_target.shape[0])
            score2= count/(count+number)
            print('准确率:', score)
            print('召回率:', score2)

结果👇

在这里插入图片描述
在这里插入图片描述

★,°:.☆( ̄▽ ̄)/$:.°★ 。撒花撒花,完美搞定定

完整代码👇

代码语言:javascript
复制
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from pandas import Series
from sklearn.feature_selection import SelectKBest, f_classif
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder
import numpy as np
from collections import Counter
data = pd.read_csv('strokePredictionData.csv')
# print(data.info())
# ## 打印数据基本信息
# print(data.describe())
## 打印数据的统计特征
# 删除id列
# data.drop("id", inplace=True, axis=1)
# # 查询表头看是否还有id,仅是验证查看
# print(data.head(0))

# # 为方便对比,创建一个1行2列的画布,figsize设置画布大小
fig, axes = plt.subplots(1, 2, figsize=(10, 5),)
# # 提供关于它的唯一值以及每个值的计数的信息
# print('计数\n', data['age'].value_counts())
# #设置画板颜色风格,Purple lover
sns.set_palette("magma")
# # 计数柱状图绘制
# sns.countplot(data=data, x='gender',ax=axes[0])
# sns.countplot(data=data, x='gender', hue='stroke',ax=axes[1])
# plt.show()

# 获取数据类型为object的列
cols = data.select_dtypes(include=['object']).columns
# 打印出object的列检查
# print(cols)
# 标签编码初始化
le = LabelEncoder()
# 将分类数据转换为数字
data[cols] = data[cols].apply(le.fit_transform)
# 随机找个object的列进行检查,看是否已将分类数据编码为数值数据
# print(data.head(10).work_type)

# 创建15*10的画布
# plt.figure(figsize=(15,10))
# print(data.corr())
# data.corr()函数说明
# data.corr()表示了data中的两个变量之间的相关性,取值范围为[-1,1],取值接近-1,表示反相关,类似反比例函数,取值接近1,表正相关
# sns.heatmap(data.corr(),annot=True,fmt='.2')
# 参数说明
# data:数据data中的两个变量之间的相关性
# annot:
# annotate的缩写,annot默认为False,当annot为True时,在heatmap中每个方格写入数据
# annot_kws,当annot为True时,可设置各个参数,包括大小,颜色,加粗,斜体字等
# fmt: 格式设置
# plt.show()

# data['avg_glucose_level'].nunique()
# sns.displot(data['avg_glucose_level'])
# sns.boxplot(data=data, x='stroke', y='avg_glucose_level')
# plt.show()

# data['age'].nunique()
# sns.displot(data['age'])
# plt.figure(figsize=(15, 7))
# sns.boxplot(data=data, x='stroke', y='age')

# data['hypertension'].nunique()
# sns.countplot(data=data, x='hypertension', hue='stroke',ax=axes[1])
# plt.show()
# data['ever_married'].nunique()
# sns.countplot(data=data, x='ever_married', hue='stroke')

# sns.countplot(data=data, x='work_type')
# sns.countplot(data=data, x='work_type', hue='stroke')
# sns.countplot(data=data,x='bmi')
# sns.boxplot(data=data,x='stroke',y='bmi')
# sns.countplot(data=data,x='smoking_status')
# sns.countplot(data=data,x='smoking_status',hue='stroke')
# plt.show()
# #处理数据空值,用0代替
# data  = data.replace(np.nan, 0)
# # 特征选择F检验(f_classif)
# #参数说明:score_func[得分方法]
# classifiers = SelectKBest(score_func=f_classif, k=5)
# # 用于计算训练数据的均值和方差
# fits = classifiers.fit(data.drop('stroke', axis=1), data['stroke'])
# # DataFrame的单元格可以存放数值、字符串等,这和excel表很像,同时DataFrame可以设置列名columns与行名index
# x = pd.DataFrame(fits.scores_)
# print(x)
# columns = pd.DataFrame(data.drop('stroke', axis=1).columns)
# # concat函数是pandas底下的方法,可以把数据根据不同的轴进行简单的融合
# # pd.concat(objs, axis=0, join='outer', join_axes=None, ignore_index=False,
# #        keys=None, levels=None, names=None, verify_integrity=False)
# # 参数说明:
# # objs:series,dataframe,或者panel构成的序列list
# # axis:0 行,1列
# fscores = pd.concat([columns, x], axis=1)
# fscores.columns = ['属性特征', '得分']
# # sort_values()是pandas中比较常用的排序方法,其主要涉及以下三个参数:
# # by : str or list of str(字符或者字符列表)
# # Name or list of names to sort by.
# # 当需要按照多个列排序时,可使用列表
# # ascending : bool or list of bool, default True
# # (是否升序排序,默认为true,降序则为false。如果是列表,则需和by指定的列表数量相同,指明每一列的排序方式)
# fscores.sort_values(by='得分', ascending=False)
# plt.show()
# # print(fscores)
# print(type(data.age))

#针对葡萄糖水平进行分箱处理
data.avg_glucose_level = pd.cut(data.avg_glucose_level,4,labels=[0,1,2,3])    # 实现等距分箱,分为4个箱
# print(data.avg_glucose_level)
#针对年龄进行分箱处理
data.age = pd.cut(data.age,4,labels=[0,1,2,3])    # 实现等距分箱,分为4个箱
# print(data.age)

# 分割数据
train_x, test_x, train_y, test_y = train_test_split(data,data['stroke'], random_state=1, test_size=0.3)
# train_test_split 函数【从 sklearn.model_selection 中调用】参数说明
# train_data:所要划分的样本特征集
# train_target:所要划分的样本结果
# test_size:样本占比,如果是整数的话就是样本的数量
# train_y:训练集中风情况【0/1】
# random_state:是随机数的种子。
# 随机数种子:是该组随机数的编号,在需要重复试验的时候,保证得到一组一样的随机数。
# 数据形式👇
# print(train_x)



#训练集数组化处理
train_x_ages=Series.tolist(train_x.age)
train_x_hypertensions=Series.tolist(train_x.hypertension)
train_x_heart_diseases=Series.tolist(train_x.heart_disease)
train_x_ever_marrieds=Series.tolist(train_x.ever_married)
train_x_avg_glucose_levels=Series.tolist(train_x.avg_glucose_level)
train_ys= Series.tolist(train_y)

#测试集数组化处理
test_x_ages=Series.tolist(test_x.age)
test_x_hypertensions=Series.tolist(test_x.hypertension)
test_x_heart_diseases=Series.tolist(test_x.heart_disease)
test_x_ever_married=Series.tolist(test_x.ever_married)
test_x_avg_glucose_levels=Series.tolist(test_x.avg_glucose_level)
test_ys= Series.tolist(test_y)
#np.vstack拼接数组
need_data=np.vstack((train_x_ages,train_x_hypertensions,train_x_heart_diseases,train_x_ever_marrieds,train_x_avg_glucose_levels,train_ys)).tolist()
test_data=np.vstack((test_x_ages,test_x_hypertensions,test_x_heart_diseases,test_x_ever_married,test_x_avg_glucose_levels,test_y)).tolist()
#检验查看处理结果
# print(need_data)
# print(test_data)


class Bayes:
    def __init__(self):
        #将数据转化为矩阵
        self.t_data = np.array(need_data)
        self.c_data=np.array(test_data)
        # print(self.c_data)
        # 使用字典方便计算时调用
        # 存储P(Y=c)的先验概率👇
        self.p_y = {}
        # 存储P(Xi=k | Y = 中风与否)的先验概率👇
        self.p_x1_y = {}
        self.p_x2_y = {}
        self.p_x3_y = {}
        self.p_x4_y = {}
        self.p_x5_y = {}
        self.predict ={}
    def train_1(self):
        # 统计data_stroke的种类及数量,用于后续计算
        count_y = Counter(self.t_data[5])
        # print(count_y)
        # 先统计y的种类,并计算P(Y=c)的先验概率,再切分训练数据
        # 计算先验概率并对应y值存入字典,然后根据不同的y切分数据,各自存入一个列表,这些列表存于字典ys
        # 统计y的种类,并计算概率,再切分训练数据
        ys = {}
        for y in count_y.keys():
            # print(count_y.keys())
            # dict_keys([0.0, 1.0])
            ys[y] = []
        # 计算先验概率并对应y值存入字典
            self.p_y[y] = count_y[y] / len(self.t_data[0])
            # print(count_y[y])# 结果为3411,166
            # print(self.p_y[y] )#先验概率结果0.9535923958624546 以及 0.04640760413754543

        # 遍历数据,根据其y存入对应列表
        for i in range(len(self.t_data[0])):
            # 将数据切分后分别存入字典中的列表,key是对应的y值
            # print(self.t_data[:, i]) #eg;[47.   0.   0.   1.  72.2  0. ]每个个体数据
            # print(self.t_data[5][i])#中风与否
            ys[self.t_data[5][i]].append(self.t_data[:, i])#将对应的中风数组与0.0,未中风数组与1.0形成字典
        print('完成数据处理,我要开始学习了')
        for item in ys.items():
            # print(ys.items())# items() 以列表返回可遍历的(键, 值) 元组数组
            # print(item)
            # print('hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh/n')
            self.train_2(item)
        print('学习完毕!可以开始预测')

    def train_2(self, _y):
        # 先把数据转化为矩阵,便于接下来切片统计运算
        # print(_y)
        data = np.array(_y[1])
        # print(_y[1])
        # 计算P(Xi=k | Y = 中风与否)的先验概率,统计每个特征的值的种类
        count_x1 = Counter(data[:, 0])
        count_x2 = Counter(data[:, 1])
        count_x3 = Counter(data[:, 2])
        count_x4 = Counter(data[:, 3])
        count_x5 = Counter(data[:, 4])
        # 检查结果
        #正是出现两部分count_x[0-5]中风与否的两种先验概率
        # print('count_x1',count_x1)
        # print('count_x2',count_x2)
        # print('count_x3',count_x3)
        # print('count_x4', count_x4)
        # print('count_x5', count_x5)
        # 计算相应的概率,存入字典
        for x1 in count_x1.keys():
            self.p_x1_y['{}_{}'.format(x1, _y[0])] = count_x1[x1] / len(data)
        for x2 in count_x2.keys():
            self.p_x2_y['{}_{}'.format(x2, _y[0])] = count_x2[x2] / len(data)
        for x3 in count_x3.keys():
            self.p_x3_y['{}_{}'.format(x3, _y[0])] = count_x3[x3] / len(data)
        for x4 in count_x4.keys():
            self.p_x4_y['{}_{}'.format(x4, _y[0])] = count_x4[x4] / len(data)
        for x5 in count_x5.keys():
            self.p_x5_y['{}_{}'.format(x5, _y[0])] = count_x5[x5] / len(data)
            # print(self.p_x5_y)
            # print(type(x5))
    def analyse_input(self):  # 计算单组数据后验概率并比较
        in_datas = input('输入x1,x2,x3,x4,x5(空格隔开):').split(' ')
        p_p = 0
        result = []
        #将输入类型str转换至与x1,x2,3,x4,5
        in_data=[1,2,3,4,5]
        in_data[0]=int(in_datas[0])
        in_data[1] = int(in_datas[1])
        in_data[2] = int(in_datas[2])
        in_data[3] = int(in_datas[3])
        in_data[4] = int(in_datas[4])
        # print(type(in_data[4]))
        for j in self.p_y.keys():
          # try:
            pp = self.p_y[j] * self.p_x1_y['{}_{}'.format(in_data[0], j)] *self.p_x2_y['{}_{}'.format(in_data[1], j)]* self.p_x3_y['{}_{}'.format(in_data[2], j)]*self.p_x4_y['{}_{}'.format(in_data[3], j)] *self.p_x5_y['{}_{}'.format(in_data[4], j)]
            # print(self.p_y[j])
            if self.p_y[j]>0.5:
                print('未中风概率为',pp)
            else:
                print('中风概率为',pp)
            if pp >= p_p:  # 观察到,对于相同的输入,可能出现两种不同预测结果(对于本次数据来说,只有两种结果),要对此做处理
                if pp > p_p:  # 若有出现更大的概率,需要把先前已有的所有结果全部替换
                    if not result:  # 开始的时候列表是空的,如果直写循环替换,其实那个循环根本不会开始。如果在循环后添加,那将会导致接下来有的结果会重复进入列表(被替换的和被添加的)
                        result.append(j)
                    else:
                        for r in range(len(result)):
                            result[r] = j
                elif p_p == pp:
                    result.append(j)
                p_p = pp
          # except:
          #   print(result)
        result = list(set(result))
        if len(result) == 1:
            print('预测结果为:{}'.format(result[0]))
        else:
            print('可能结果如下:', end='')
            for e in result:
                print(e)

    def analyse_input2(self):  # 计算测试集数据后验概率并比较
        p_p = 0
        result = []
        in_data=[1,2,3,4,5]
        # print(self.c_data.shape[1])
        for m in range(0,self.c_data.shape[1]):
            #将输入类型str转换至与x1,x2,x3,x4,x5
            in_data[0]=int(self.c_data[0,m])
            in_data[1] = int(self.c_data[1,m])
            in_data[2] = int(self.c_data[2,m])
            in_data[3] = int(self.c_data[3,m])
            in_data[4] = int(self.c_data[4,m])
            # print(type(in_data[4]))
            for j in self.p_y.keys():
              # try:
                pp = self.p_y[j] * self.p_x1_y['{}_{}'.format(in_data[0], j)] *self.p_x2_y['{}_{}'.format(in_data[1], j)]* self.p_x3_y['{}_{}'.format(in_data[2], j)]*self.p_x4_y['{}_{}'.format(in_data[3], j)] *self.p_x5_y['{}_{}'.format(in_data[4], j)]
                # print(self.p_y[j])
                if self.p_y[j]>0.5:
                    print('未中风概率为',pp)
                else:
                    print('中风概率为',pp)
                if pp >= p_p:  # 观察到,对于相同的输入,可能出现两种不同预测结果(对于本次数据来说,只有两种结果),要对此做处理
                    if pp > p_p:  # 若有出现更大的概率,需要把先前已有的所有结果全部替换
                        if not result:  # 开始的时候列表是空的,如果直写循环替换,其实那个循环根本不会开始。如果在循环后添加,那将会导致接下来有的结果会重复进入列表(被替换的和被添加的)
                            result.append(j)
                        else:
                            for r in range(len(result)):
                                result[r] = j
                    elif p_p == pp:
                        result.append(j)
                    p_p = pp
              # except:
              #   print(result)
            result = list(set(result))
            # print('hhhhhresult',result)
            if len(result) == 1:
                print('预测结果为:{}'.format(result[0]))
                self.predict[m]=result[0]
                # print(m)#0-1532
            else:
                print('可能结果如下:', end='')
                for e in result:
                    print(e)


    #计算得分
    def score(self, test_target):
                count=0
                number=0
                # print(np.array(test_target))
                # print(np.array(self.predict.values()))
                # 数据格式转换
                T=str(list(self.predict.values()))
                s=str(list(np.array(test_target)))
                # print(type(n),type(s))
                for i in range(0, test_target.shape[0]):
                    print(i)
                    print(s[i],T[i])
                    if s[i] == T[i]:
                        count += 1
                    if s[i] !=T[i]:
                        number+=1
                score = count / (test_target.shape[0])
                score2= count/(count+number)
                print('准确率:', score)
                print('召回率:', score2)
answer = Bayes()
answer.train_1()
answer.analyse_input2()
answer.score(test_y)
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-09-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 贝叶斯中风预测详解--python
  • 1. 内容描述
    • 1.1 字段描述
      • 1.2 Exploratory Data Analysis探索性数据分析
        • 1.2.1数据整体信息以及统计特征
        • 1.2.2 id
        • 1.2.3 gender性别
        • 1.2.4 age年龄
        • 1.2.5 Hypertension高血压
        • 1.2.6 heart_disease心脏病
        • 1.2.7 ever_married已婚与否
        • 1.2.8 work_type工作类型
        • 1.2.9 Residence_type居住类型
        • 1.2.10 avg_glucose_level患者体内的平均血糖水平
        • 1.2.11 bmi
        • 1.2.12 smoking_status吸烟状况
      • 1.3 特征工程
        • 1.3.1 标签编码
        • 1.3.2 特征相关性检查
        • 1.3.3 连续型数据处理
      • 1.4 贝叶斯模型描述
        • 1.5 数据集拆分
          • 1.6 模型创建
            • 1.6.1 先验条件计算
            • 1.6.2 后验条件计算(单组)
            • 1.6.3 后验条件计算(测试集)
          • 1.7 结果评估
            • 1.7.1准确率计算
            • 1.7.2 召回率计算
        • ★,°:.☆( ̄▽ ̄)/$:.°★ 。撒花撒花,完美搞定定
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档