贝叶斯实例中风预测详解--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数据整体信息以及统计特征

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

代码👇

# 删除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),对此采用计数柱状图来进行比较。

代码👇

# 为方便对比,创建一个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年龄

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

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高血压

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

代码👇

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已婚与否

代码👇

data['ever_married'].nunique()
sns.countplot(data=data, x='ever_married', hue='stroke')
plt.show()

结果👇
在这里插入图片描述

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

1.2.8 work_type工作类型

代码👇

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居住类型

代码👇

sns.countplot(data=data,x='Residence_type')
plt.show()

结果👇
在这里插入图片描述

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

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

代码👇

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

代码👇

sns.countplot(data=data,x='bmi')
sns.boxplot(data=data,x='stroke',y='bmi')
plt.show()

结果👇
在这里插入图片描述
在这里插入图片描述

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

1.2.12 smoking_status吸烟状况

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

sns.countplot(data=data,x='smoking_status',hue='stroke')
plt.show()

结果👇
在这里插入图片描述
在这里插入图片描述

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

1.3 特征工程

1.3.1 标签编码

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

# 获取数据类型为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 热图
代码👇
# 创建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()
结果👇

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

1.3.2.2 SelectKBest and F_Classif

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

#处理数据空值,用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 连续型数据处理

代码👇

#针对葡萄糖水平进行分箱处理
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)
代码👇

# 分割数据
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数据集
代码👇

#数组化处理
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,…)表示各种原因发生的可能性大小
代码👇

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之后,再对各种原因概率的新认识,故称,在此采用输入方式进行检验查看数据预测状况

代码👇

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 后验条件计算(测试集)

代码👇

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准确率计算

         准确率(Precision) =  系统检索到的相关事件 / 系统所有检索到的事件总数
#计算得分
		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) = 系统检索到的相关事件 / 系统所有相关的事件总数
代码👇

#计算得分
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)

结果👇
在这里插入图片描述

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

完整代码👇

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)

评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

司六米希

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值