今天中午开始实现决策树ID3的实现,KNN的缺点是不能体现数据的意义,全部数据全部归一化,决策树能体现数据的意义,根据数据分类。
先用一个一般的数据测试一下,隐形眼镜类型只不过是增加了一步从文件中提取数据这一步,其他步骤一模一样。
用matplotlib图形化展示效果特别好,但是特别费劲,没有一些特别麻烦的函数,所以这里不再给出,时间问题没能整明白,参考《机器学习实战》。
将在以后的学习里加入CART这一流行的决策树。
step1:
创建一个可以测试的数据,dataset和lavels
#创建数据集
def createDataSet():
dataset = ([[1, 1, 'yes'],
[1, 1, 'yes'],
[1, 0, 'no'],
[0, 1, 'no'],
[0, 1, 'no']])
lavels = ['no surfacing', 'flippers']
return dataset, lavels
有2个特性,2种标签。
step2:
计算数据集的香农熵,公式不再赘述。这里先把函数写在ID3.py,留待下面用。
#计算数据集的香农熵
def countShannonEnt(dataset):
datalen = len(dataset)
diclavels = {}
for i in dataset:
templavel = i[-1]
if templavel not in diclavels.keys():
diclavels[templavel] = 0
diclavels[templavel] += 1
shannonent = 0.0
for key in diclavels.keys():
temp = float(diclavels[key])/datalen
shannonent -= temp * log(temp, 2)
return shannonent
根据所传入的dataset集中的结果标签分布计算混乱度,也就是香农熵。
step3:
dataset为要划分的数据集,axis为要划分特性的列,value为划分的特征值。
#根据特征划分数据集 参数为数据集,特性列数,特征值
def splitDataSet(dataset, axis, value):
newdataset = []
for i in dataset:
if i[axis] == value:
newline = i[ : axis]
newline.extend(i[axis + 1 : ])
newdataset.append(newline)
return newdataset
step4:
从dataset中选择一个最好的特性,计算信息增益,选择信息增益最大的那个特性。
#选择最好的特性进行分类
def chooseBestFeatureToSplit(dataset):
featruenums = len(dataset[0]) - 1
baseshannonent = countShannonEnt(dataset)
bestfeatrue = -1
bestgain = 0.0
for i in range(featruenums):
featruelist = [x[i] for x in dataset]
newfeatruelist = set(featruelist)
newent = 0.0
for ii in newfeatruelist:
subdataset = splitDataSet(dataset, i, ii)
subshannon = countShannonEnt(subdataset)
p = len(subdataset)/float(len(dataset))
newent += p * subshannon
tempgain = baseshannonent - newent
if tempgain >= bestgain:
bestgain = tempgain
bestfeatrue = i
return bestfeatrue
step5:
当没有特性可以选取时,但数据集标签还没有统一,进行类似KNN的投票。
#类似KNN的投票器,当没有特性进行分类的时候选当前最多的标签作为该数据集的标签
def noFeatrueToSplit(mat):
templavels = {}
for i in mat:
if i not in templavels.keys():
templavels[i] = 0
templavels[i] += 1
sortlavels = sorted(templavels.iteritems(),\
key = operator.itemgetter(1), reverse = True)
return sortlavels[0][0]
step6:
树的创建:
先是判断是不是标签统一,然后判断是不是没有特性可以选取,没有特性可以选取的话进行step5的投票。
然后选取一个最优特性,删除特性标签中的最优特性对应的标签,然后递归进行树的划分,3个小细节。
1:创建树的时候特性标签要重新复制一个,因为引用传递的话会改变原数组的值,会影响后面的划分。
2:用set进行压缩重复的元素,这样可以得到该特性的几种分类。
3:用递归创建。
#树的创建
def createTree(dataset, lavels):
lavelslist = [ll[-1] for ll in dataset]
if lavelslist.count(lavelslist[0]) == len(lavelslist):
return lavelslist[0]
if len(dataset[0]) == 1:
return noFeatrueToSplit(dataset)
bestfeatrue = chooseBestFeatureToSplit(dataset)
bestlavel = lavels[bestfeatrue]
myTree = {bestlavel : {}}
#删除标签里这一行,因为数据集中接下来分类的时候也要删除最佳特性这一行
del (lavels[bestfeatrue])
featruetimes = [ll[bestfeatrue] for ll in dataset]
featruenum = set(featruetimes)
for i in featruenum:
sublavels = lavels[ : ]
myTree[bestlavel][i] = createTree(splitDataSet(dataset, \
bestfeatrue, i), sublavels)
return myTree
step7:
察看测试结果:
atep8:
决策树的分类函数根据之前得出的myTree,test参数为我们要进行测试的数据。
#决策树的分类函数
def type(myTree, lavels, test):
#取得该树的分类特性标签
firstfeatrue = myTree.keys()[0]
feaindex = lavels.index(firstfeatrue)
seconddict = myTree[firstfeatrue]
for keytemp in seconddict.keys():
if keytemp == test[feaindex]:
if isinstance(seconddict[keytemp], dict):
reslavel = type(seconddict[keytemp], lavels, test)
else:
reslavel = seconddict[keytemp]
return reslavel
接下来进行隐形眼镜的预测:
step9:
从文本文件中读取数据并现实结果:
import ID3
f = open('lenses.txt')
lines = f.readlines()
lines
lenses = [line.strip().split('\t') for line in lines]
lenses
lenseslavels = ['age', 'prescript', 'astigmatic', 'tearRate']
lensesTree = ID3.createTree(lenses, lenseslavels)
lensesTree
结果正确,缺点是结果看着不好辨认,不如图形界面看起来直观,所以有时间看一下matplotlib图形化是怎么实现的,才算是真正完成了任务。
时间紧急,明天抓紧时间看基于概率论的分类方法:朴素贝叶斯。