朴素贝叶斯算法之processed.cleveland.data数据集


接触数据挖掘不久,对数据挖掘的理解还处于皮毛的状态,刚尝试实现了朴素贝叶斯算法并对heart disease数据集进行分类测试,抱着学习的态度书写此文分享大家,希望读者能对算法不当之处和可以优化的地方予以包含,更希望能给予宝贵的建议,深表感谢!

朴素贝叶斯分类基本内容

贝叶斯分类法是统计学分类方法。
可以预测隶属关系的概率。
贝叶斯分类基于贝叶斯定理。
朴素贝叶斯分类法可以与决策树和经过挑选的神经网络分类器相媲美。
用于大型数据库,高准确率和高速度。

朴素贝叶斯分类假设

假定一个属性值在给定类上的影响独立于其他属性的值。
——类条件独立性(为了简化计算,所以称朴素的)

贝叶斯定理

设X是一个类标号未知的数据样本,H表示一个假设:数据样本X属于某个特定的类C。
要求确定P(H|X),即给定观测数据样本X的情况下假设H成立的概率。

P(H|X)是后验概率,或在条件X下,H的后验概率。
P(H)是先验概率,或H的先验概率。
P(X|H)是条件H下,X的后验概率。
P(X)是X的先验概率。

贝叶斯定理:P(H|X) = P(X|H)✖P(H)/P(X)
P(X)、P(H)、P(X|H) 可以有给定的数据估计。

贝叶斯分类的基本思想

设输入空间是n维向量的集合,取其中m个样本做训练数据集,表示为S={S1,S2,…,Sm},其中每个样本Si都是一个n维向量{x1,x2,…xn};输出空间是类标记的集合,表示为Y={C1,C2,…,Cn},取自输入空间的每个样本Si都与输出空间的一个类Ci相对应。
当给定另外一个类别未知的数据样本X,可以把X分到后验概率最大的类中,也就是用最高的条件概率P(Ci|X)来预测X的类别。
根据贝叶斯定理,后验概率计算过程为P(H|X) = P(X|H)✖P(H)/P(X)
P(X)对所有类为常数,所以只需要使P(Ci)=P(X|Ci)P{Ci}最大化,其中P(Ci)=|Ci|/m

朴素贝叶斯分类过程

朴素贝叶斯分类时,假设输入变量都是条件独立的,通过在训练数据上学习得到模型,计算出每个类别的先验概率和条件概率,依照模型计算后验概率P(Ci|X),将后验概率最大的类作为输入变量所属的类输出。

朴素贝叶斯分类算法

算法:朴素贝叶斯分类算法
输入:
数据集S是训练元组和对应类标号的集合
待分类的数据X
输出:数据X所属的类别
方法:
根据数据集S计算每个类别Ci的先验概率P(Ci)。
根据数据集S计算各个独立特征X在分类中的条件概率P(X|Ci)
对于待定的输入数据X,计算其相应属于待定分类的条件概率P(Ci|X)
选择条件概率最大的类别作为该输入数据X的类别返回

朴素贝叶斯算法实现

import pandas as pd
测试数据
data_test={
    'S1':[1,1,1,1,1,2,2,2,2,2,3,3,3,3,3],
    'S2':['S','P','P','S','S','S','P','P','Q','Q','Q','P','P','Q','Q'],
    'C':[-1,-1,1,1,-1,-1,-1,1,1,1,1,1,1,1,-1]
}
data_df=pd.DataFrame(data_test)
data_df

#手算结果
P_Ci_test={1:0.6,-1:0.4}
P_XinCi_test={
    1:{'S1':{1:2/9,2:3/9,3:4/9},'S2':{'S':1/9,'P':4/9,'Q':4/9}},
    -1:{'S1':{1:3/6,2:2/6,3:1/6},'S2':{'S':3/6,'P':2/6,'Q':1/6}}
}
print(P_Ci_test)
print(P_XinCi_test)

函数:朴素贝叶斯,计算P(Ci)、P(X|Ci)
'''
朴素贝叶斯,计算P(Ci)、P(X|Ci)
输入:
    S:训练元组和对应类标号的集合(dataframe类型)
    labelName:标签列的名字(字符串)
返回值:
    字典P(Ci)、P(X|Ci)
    列表labels,标签集合
    
'''
def bayesCalcP(S,labelName):
    
    labels=set(S[labelName])#标签集合
    num_Ci=dict.fromkeys(labels,0)#保存每种标签对应的个数(字典)
    P_Ci=dict.fromkeys(labels,0)#保存每种标签对应的概率(字典)
    num_S=S.count()[0]#S中元组的总数
    
    #获取S中除标签列外的所有列名
    li=list(S)
    li.remove(labelName)
    
    P_XinCi=dict.fromkeys(labels,None)#保存每个标签下,属性中每个特征X的概率{0:None,1:None,.....}
    
    for i in labels:#心脏病诊断[0,1,2,3,4]
        #计算每一个标签的的概率,即P(Ci)
        S_Ci=S[S[labelName]==i]#标签为Ci的元组(pandas类型)
        num_Ci[i]=S_Ci.count()[0]#标签i的元组总数
        P_Ci[i]=num_Ci[i]/num_S#P(Ci)=count(Ci)/count(S)
        
        P_shuxingInCi=dict.fromkeys(li,None)#{年龄:None,性别:None,......}
        
        #计算各个独立特征X在分类中的条件概率P(X(年龄)=1|Ci)=count(X(年龄)=1)/count(Ci)
        for j in li:#['年龄','性别',......]
            val_CiinX=set(S_Ci[j])#j==年龄时,{1岁,2岁......}
            P_XinShuxing=dict.fromkeys(val_CiinX,0)#保存每个属性下每个特征X的概率,{0岁:0,1岁:0,......}
            for k in val_CiinX:
                P_XinShuxing[k]=S_Ci[S_Ci[j]==k].count()[0]/num_Ci[i]#属性j中值为k的概率,即P(X|Ci)
            P_shuxingInCi[j]=P_XinShuxing
        P_XinCi[i]=P_shuxingInCi
    return P_Ci,P_XinCi,labels
测试 bayesCalcP()函数
P_Ci,P_XinCi,labels=bayesCalcP(data_df,'C')
print(P_Ci)
print(P_XinCi)
print(labels)

#比较函数返回结果是否等于正确结果
print(P_Ci==P_Ci_test)
print(P_XinCi==P_XinCi_test)

True
True

函数:朴素贝叶斯,分类
'''
朴素贝叶斯,分类
输入:
    X:待分类的数据(列表)
    labels:标签集合(列表)
    names:除标签列外的所有属性名(列表)
    P_Ci:每个类别Ci的先验概率P(Ci)(字典)
    P_XinCi:各个独立特征X在分类中的条件概率P(X|Ci)(字典)
输出:
    X所属于的类别
'''
def bayesClassify(X,labels,names,P_Ci,P_XinCi):
    P_max=0#保存当前最大的概率
    #保存当前最大概率下的类
    for i in labels:
        P=P_Ci[i]#P保存计算所得概率
        for j in names:
            P=P*P_XinCi[i][j].get(X[names.index(j)],0)
        if P_max<P:
            P_max=P
            C_i=i
    return C_i
测试bayesClassify()函数
labels=[1,-1]
X=[2,'S'] #属于标签为-1的类
names=['S1','S2']
bayesClassify(X,labels,names,P_Ci_test,P_XinCi)

-1

心脏病数据集的朴素贝叶斯分类

import numpy as np
names=['年龄','性别','类型','静态血压','血清胆甾醇','空腹血糖','静息心电图','最大心率','运动心绞痛','ST压低','ST段峰值斜率','主要血管数','thal','心脏病的诊断']
data=pd.read_csv("processed.cleveland.data",sep=",",header=None,names=names)
data.head()

删除缺失值的行
#删除缺失值的行
data=data.replace(to_replace="?",value=np.nan)
data.dropna(inplace=True)
data.head()

数据离散化
def data_cut1(data,name,bins,labels):
    columnNum=data.columns.values.tolist().index(name)#需要离散化的行号
    sr=pd.cut(data[name],bins=bins,labels=labels)
    data.drop(name,axis=1,inplace=True)
    data.insert(columnNum,name,sr)
    
def data_cut2(data,name,boxNum,labels):
    columnNum=data.columns.values.tolist().index(name)#需要离散化的行号
    sr=pd.cut(data[name],boxNum,labels=labels)
    data.drop(name,axis=1,inplace=True)
    data.insert(columnNum,name,sr)
data_cut2(data,'年龄',3,['壮年','中年','老年'])
data_cut1(data,'静态血压',[0,90,140,200],['偏低','正常','偏高'])
data_cut1(data,'血清胆甾醇',[0,200,300,400,500],['过低','低','中','高'])
data_cut1(data,'最大心率',[0,100,170,300],['过慢','正常','过快'])
data_cut1(data,'ST压低',[-0.1,1,3,6],['低','正常','高'])
data.head()

设置训练数据集和测试数据集
#训练数据集
data_train=data[data.index<=250]
data_train.head()
#测试数据集
data_test=data[data.index>250]
data_test.head()
#获得除去标签的数据
data_test1=data_test.drop({'心脏病的诊断'},axis=1)
data_test1.head()
#获取对应的标签
data_test_labels=data_test['心脏病的诊断']
data_test_labels.head()
#计算概率
P_Ci,P_XinCi,labels=bayesCalcP(data_train,'心脏病的诊断')
P_Ci
sum_right=0 #预测正确的个数
sum_wrong=0 #预测错误的个数

#将测试集转化为二维list
data_test_list = data_test1.values.tolist()
#将测试集对应的标签转化成list
data_test_labels_list=data_test_labels.values.tolist()
names=['年龄','性别','类型','静态血压','血清胆甾醇','空腹血糖','静息心电图','最大心率','运动心绞痛','ST压低','ST段峰值斜率','主要血管数','thal']
for i in range(len(data_test_list)):
    if bayesClassify(data_test_list[i],list(labels),names,P_Ci,P_XinCi)==data_test_labels_list[i]:
        sum_right+=1
    else:
        sum_wrong+=1
print("预测正确")
print("个数:",sum_right)
print("比例:",sum_right/len(data_test_list))

print("预测错误")
print("个数:",sum_wrong)
print("比例:",sum_wrong/len(data_test_list))

结果分析

#经回顾代码和朴素贝叶斯原理,
#猜测预测结果差的原因,是在对心脏病数据集离散化的时候,想当然画分的距离,划分区间与实际情况差异过大
#朴素贝叶斯本身弱点:算法假设属性之间相互独立,但心脏病数据集中属性之间存在关联

总结

程序中需要用到比较多的变量,在变量命名时难以命名出可读性比较强、见文知意的名字;程序中的注释比较混乱;总之觉得写出来的程序可读性比较差一点。

由于对心脏病方面的知识不够,尽管通过网络等渠道了解了一些相关信息,但在离散化数据的时候仍不能保证具有实际意义。

以上就是全部的分享,感谢读者对不足之处的谅解、希望能给与宝贵的意见和建议!

  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值