我们是否可以通过计算样例分类的概率来对样例进行分类呢,即一个样例分为正例的概率为0.9,分为反例的概率为0.5,那么我们将该样例分为正例。

那么我们的目标就是求P(正)和P(反)。

每一个样例都是由多个属性组成的,我们根据属性的取值来计算概率,这是明显的条件概率P(正|X),P(反|X)。X表示各个属性组成的属性向量(x1, x2, x3,......,xn)

若各个属性之间是独立的,即各个属性独立的对分类结果产生影响,那么P(正|X)或P(反|X)的计算式为(C表示分类标签)

在数据集中我们能够通过统计得出的是,P(x|C)即分类为C的属性取值为x的概率,要将P(x|C)转换为P(c|X),我们就要用到贝叶斯公式,这也是该分类方法名字的由来。

贝叶斯公式如下:

 接下来,我们来实现一下:

数据集:

编号	色泽	根蒂	敲击	纹理	脐部	触感	好瓜
0	青绿	蜷缩	浊响	清晰	凹陷	硬滑	是
1	乌黑	蜷缩	沉闷	清晰	凹陷	硬滑	是
2	乌黑	蜷缩	浊响	清晰	凹陷	硬滑	是
3	青绿	蜷缩	沉闷	清晰	凹陷	硬滑	是
4	浅白	蜷缩	浊响	清晰	凹陷	硬滑	是
5	青绿	稍蜷	浊响	清晰	稍凹	软粘	是
6	乌黑	稍蜷	浊响	稍糊	稍凹	软粘	是
7	乌黑	稍蜷	浊响	清晰	稍凹	硬滑	是
8	乌黑	稍蜷	沉闷	稍糊	稍凹	硬滑	否
9	青绿	硬挺	清脆	清晰	平坦	软粘	否
10	浅白	硬挺	清脆	模糊	平坦	硬滑	否
11	浅白	蜷缩	浊响	模糊	平坦	软粘	否
12	青绿	稍蜷	浊响	稍糊	凹陷	硬滑	否
13	浅白	稍蜷	沉闷	稍糊	凹陷	硬滑	否
14	乌黑	稍蜷	浊响	清晰	稍凹	软粘	否
15	浅白	蜷缩	浊响	模糊	平坦	硬滑	否
16	青绿	蜷缩	沉闷	稍糊	稍凹	硬滑	否

我们训练和测试都使用它。

 读入数据:

def loaddataset(Filename):
	fp = open(Filename)
	a = fp.readlines()
	fp.close()
 
	dataset = []
	for i in a[1:]:
		b = i.strip().split()
		dataset.append(b[1:])
	#返回数据和属性标签
	return dataset, a[0].strip().split()[1:]

dataset显示如下:

分类标签显示如下:

['色泽', '根蒂', '敲击', '纹理', '脐部', '触感', '好瓜']

 统计各个属性取值的正例个数比例和反例个数比例:

def statistics(dataset, n):
	#记录统计结果
	count = {}
	for i in dataset:
		if i[n] not in count:
			#列表第一个位置为正例个数,第二个位置是反例个数
			if i[-1] == '是':
				count[i[n]] = [1, 0]
			else:
				count[i[n]] = [0, 1]
		else:
			if i[-1] == '是':
				count[i[n]][0] += 1
			else:
				count[i[n]][1] += 1
	#计算各个属性取值为正例的概率和反例的概率
	for i in count:
		n = count[i][0] + count[i][1]
		count[i][0] = count[i][0] / n
		count[i][1] = count[i][1] / n
	return count

该函数每次只统计一个属性的情况,我们以色泽为例 ,其统计结果为

{'青绿': [0.5, 0.5], '乌黑': [0.6666666666666666, 0.3333333333333333], '浅白': [0.2, 0.8]}

我使用列表保存所有属性的统计情况:

​
count_sum = []
for i in range(len(dataset[0])-1):
	count_sum.append(statistics(dataset, i))
 
​

统计数据集中正反例的个数:

a = list(np.array(dataset)[:,-1])
y = a.count('是')
n = a.count('否')

预测:

    #记录正确预测的个数
	rightcount = 0
	for i in range(len(dataset)):
		#记录分为正类的概率
		py = 1
		#记录分为反类的概率
		pn = 1
		for j in range(len(dataset[i])-1):
			b = count_sum[j]
			py *= b[dataset[i][j]][0]
			pn *= b[dataset[i][j]][1]
		py *= y / (y + n)
		pn *= n / (y + n)
		if py >= pn:
			flag = '是'
		else:
			flag = '否'
		if flag == a[i]:
			rightcount += 1
		print('%s  %s'%(flag, a[i]))
	print("正确率为%s"%(rightcount / len(dataset)))

预测结果如下:

是  是
是  是
是  是
是  是
是  是
是  是
否  是
是  是
否  否
否  否
否  否
否  否
否  否
否  否
是  否
否  否
否  否
正确率为0.8823529411764706
[Finished in 0.4s]

完整代码如下,数据集和原文件已上传至我的资源https://download.csdn.net/download/qq_41398808/11242762

import numpy as np
 
def loaddataset(Filename):
	fp = open(Filename)
	a = fp.readlines()
	fp.close()
 
	dataset = []
	for i in a[1:]:
		b = i.strip().split()
		dataset.append(b[1:])
	#返回数据和属性标签
	return dataset, a[0].strip().split()[1:]
 
def statistics(dataset, n):
	#记录统计结果
	count = {}
	for i in dataset:
		if i[n] not in count:
			#列表第一个位置为正例个数,第二个位置是反例个数
			if i[-1] == '是':
				count[i[n]] = [1, 0]
			else:
				count[i[n]] = [0, 1]
		else:
			if i[-1] == '是':
				count[i[n]][0] += 1
			else:
				count[i[n]][1] += 1
	#计算各个属性取值为正例的概率和反例的概率
	for i in count:
		n = count[i][0] + count[i][1]
		count[i][0] = count[i][0] / n
		count[i][1] = count[i][1] / n
	return count
 
 
if __name__ == '__main__':
	dataset, labelset = loaddataset('watermelon2.0.data')
	count_sum = []
	for i in range(len(dataset[0])-1):
		count_sum.append(statistics(dataset, i))
	a = list(np.array(dataset)[:,-1])
	y = a.count('是')
	n = a.count('否')
 
	#记录正确预测的个数
	rightcount = 0
	for i in range(len(dataset)):
		#记录分为正类的概率
		py = 1
		#记录分为反类的概率
		pn = 1
		for j in range(len(dataset[i])-1):
			b = count_sum[j]
			py *= b[dataset[i][j]][0]
			pn *= b[dataset[i][j]][1]
		py *= y / (y + n)
		pn *= n / (y + n)
		if py >= pn:
			flag = '是'
		else:
			flag = '否'
		if flag == a[i]:
			rightcount += 1
		print('%s  %s'%(flag, a[i]))
	print("正确率为%s"%(rightcount / len(dataset)))