决策树是一种常见的机器学习方法,也称作“判定树”。决策树是根据树结构来进行决策的,决策过程中提出的每一个判定都是对某个属性的“测试”,每个测试的结果或是导出最终结论,或者导出进一步的判定问题,在测评那种数据划分方式是做好的数据划分的时候,有多种测量方法,其中有一种计算信息增益的方法,叫作香农熵。“信息熵”是用来度量两种概率分布的差异,假设当前样本的集合D中第k类样本所占的比例为pk(k=1,2,...,|y|),则D的信息熵为:
下面是计算给定数据集的香农熵:
from math import log
def calcShannonEnt(dataSet):
numEntries=len(dataSet) #numEntries得到的是数据集的大小,保存实例总数
labelCounts={}
for featVec in dataSet: #为了所有数据创建一个字典,方便后面的求每种类的概率
currentLabel=featVec[-1] #创建一个数据字典,健值是最后一列的值,取该数据的特征标签
if currentLabel not in labelCounts.keys(): #如果该特征标签不存在
labelCounts[currentLabels]=0 #则扩展字典并将当前标签加入字典
labelCounts[currentLabels]+=1 #记录每个类别出现的次数
shannonEnt=0.0
for key in labelCounts:
prob=float(labelCounts[key])/numEntries #每个类别除以总数,是该类别的概率,也就是上面式子中的pk
shannonEnt-=prob*log(prob,2) *这一行就是上述pk乘以以2为底的对数
return shannonEnt
测试上面的代码的结果为:
下面开始划分数据集:
这个函数的作用是将第axis列中等于value的数据集划分出来
def splitDataSet(dataSet,axis,value): #可以看到这个函数有三个输入参数:待划分的数据集,划分数据集的特征以及需要返回的特征的值
retDataSet=[] #因为该函数在同一个数据集上调用了很多次,所以定义一个新的列表对象
for featVec in dataSet: #遍历数据集中的每个元素的特征
if featVec[axis]==value: #如果该数据的这个特征的值符合要求
reducedFeatVec=featVec[:axis] #axis前几行
reducedFeatVec.extend(featVec[axis+1:]) #跳过axis,添加之后的之后的
retDataSet.append(reducedFeatVec)
return retDataSet
下面是选择最好的数据集划分方式的代码:
def chooseBestFeatureToSplit(dataSet): #计算出该数据集的最好的划分数据集的特征
numFeatures=len(dataSet[0])-1 #数据的最后一列为特征
baseEntropy=calcShannonEnt(dataSet)
bestInfoGain=0.0;bestFeature=-1
for i in range(numFeatures):
featList=[example[i] for example in dataSet] #创建数据集中的分类标签列表
uniqueVals=set(featList) #这里的set是一个无序不重复的集合
newEntropy=0.0
for value in uniqueVals:
subDataSet=splitDataSet(dataSet,i,value) #用这个value值进行划分的数据集
prob=len(subDataSet)/float(len(dataSet)) #返回这个在所有中的比例
newEntropy+=prob*calcShannonEnt(subDataSet)
infoGain=baseEntropy-newEntropy #这几行代码是在计算信息增益
if(infoGain>bestInfoGain): #如果这样的划分比之前最好的划分方式得到的信息增益多,则替换划分方法为当前划分方法
bestInfoGain=infoGain
bestFeature=i
return bestFeature #返回最终的特征
返回出现次数最多的分类名称:
def majorityCnt(classList):
classCount={}
for vote in classList:
if vote not in classCount.keys():classCount[vote]=0 #如果这个类别还没有出现过,则创建一个存储这个类的数量的组
classCount[vote]+=1
sortedClassCount=sorted(classCount.iteritems(),key=operator.itemgetter(1),reverse=True) #进行一个排序
return sortedClassCount[0][0]
递归构建决策树的代码:
def createTree(dataSet,labels):
classList=[example[-1] for example in dataSet] #包含数据集的所有类标签
if classList.count(classList[0])==len(classList): #第一个类标签的数量与所有类标签的数量相同,也就是所有类标签完全相同,
return classList[0] #直接返回这个标签
if len(dataSet[0])==1: #使用完所有的特征,仍然不能讲数据集划分成仅包含一个类别的分组,返回数量最多的类别
return majorityCnt(classList)
bestFeat=chooseBestFeatureToSplit(dataSet) #最好的划分特征
bestFeatLabel=labels[bestFeat] #最好的划分特征的标签
myTree={bestFeatLabel:{}}
del(labels[bestFeat])
featValues=[example[bestFeat] for example in dataSet] #最好划分的所有的属性值
uniqueVals=set(featValues)
for value in uniqueVals:
subLabels=labels[:]
myTree[bestFeatLabel][value]=createTree(splitDataSet\
(dataSet,bestFeat,value),subLabels)
return myTree
下面是一个使用文本注解绘制树节点:
import matplotlib.pyplot as plt
decisionNode=dict(boxstyle="sawtooth",fc="0.8")
leafNode=dict(boxstyle="round4",fc="0.8")
arrow_args=dict(arrowstyle="<-")
def plotNode(nodeTxt,conterPt,parentPt,nodeType):
createPlot.ax1.annotate(nodeTxt,xy=parentPt,\
xycoords='axes fraction',\
xytext=conterPt,textcoords='axes fraction',\
va="center",ha="center",bbox=nodeType,arrowprops=arrow_args)
def createPlot():
fig=plt.figure(1,facecolor='white')
fig.clf()
createPlot.ax1=plt.subplot(111,frameon=False)
plotNode(U'a',(0.5,0.1),(0.1,0.5),decisionNode)
plotNode(U'a',(0.8,0.1),(0.3,0.8),leafNode)
plt.show()
产生的效果如下;