3决策树
3.1信息增益
在划分数据集之前之后信息发生的变化称为信息增益。计算每个特征值划分数据集获得的信息增益,选择信息增益最高的特征作为主要特征。
3.1.1熵
熵定义为信息的期望值
#香农熵计算
from math import log
def calcShannonEnt(dataSet):
numEntries = len(dataSet) #实例总数
labelCounts = {}
for featVec in dataSet:
currentLabel = featVec[-1]
if currentLabel not in labelCounts.keys():
labelCounts[currentLabel] = 0
labelCounts[currentLabel] +=1 #类标签出现的频率
shannonEnt = 0.0
for key in labelCounts:
prob = float(labelCounts[key])/numEntries #概率P
shannonEnt -=prob * log(prob,2)
return shannonEnt
#示例:简单鱼鉴定数据集
def createDataSet():
dataSet = [[1,1,'yes'],
[1,1,'yes'],
[1,0,'no'],
[0,1,'no'],
[0,1,'no']]
labels = ['no surfacing','flippers']
return dataSet, labels
在python命令提示符输入下列命令:>>>

增加第三个名为maybe的分类,测试熵的变化。可以发现混合的数据越多,熵越大。
3.1.1划分数据集-信息增益
对每个特征划分数据集的结果计算一次信息熵,然后判断按照那个特征划分数据集是最好的划分方式。即选择信息增益最大的特征。#按照给定特征划分数据集
def splitDataSet(dataSet, axis, value): #三个参数:待划分的数据集、划分数据集的特征、需要返回的特征的值
retDataSet = [] #创建新的list对象
for featVec in dataSet:
if featVec[axis] == value:
reducedFeatVec = featVec[:axis]
reducedFeatVec.extend(featVec[axis+1:])
retDataSet.append(reducedFeatVec)
return retDataSet
利用前面简单鱼样本数据myDat测试函数splitDataSet()。
In [36]行意思是:对myDat数据集按第一个特征划分,如果第一个特征等于1,则将数据抽取出来。(myDat,0,1),0表示第一个特征,1表示如果第一个特征的值等于1。
同理,按第一个特征为0划分,结果如下
接下来,我们遍历整个数据集,循环计算香农熵和splitDataSet(),找到最好的特征划分方式。
#计算信息增益,选择最好的特征
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)
newEntropy = 0.0
for value in uniqueVals:
subDataSet = splitDataSet(dataSet,i,value) #划分数据集
prob = len(subDataSet)/float(len(dataSet))
newEntropy += prob * calcShannonEnt(subDataSet) #新熵
infoGain = baseEntropy - newEntropy
if(infoGain > bestInfoGain): #选择信息增益最大的
bestInfoGain = infoGain
bestFeature = i
return bestFeature
函数调用的数据有要求:
1、数据必须是有列表元素组成的列表,且所有列表元素长度相同;
2、数据最后一列或者每个实例的最后一个元素是当前实例的类别标签。可以是数值型或字符型。
测试结果告诉我们,第0个特征是最好的用于划分数据集myDat的特征。即数据集的第一维特征。
上述是决策树的子模块。下面我们将递归构建决策树。
递归结束的条件是:程序遍历完所有划分数据集的属性,或者每个分支下的所有实例都具有相同的分类。
如果数据集已经处理完了所有属性,但是类标签依然不是唯一的,此时,采用多数表决的方法决定叶子节点的分类。定义叶子节点和创建树的函数代码如下:
# 多数表决 决定叶子节点的分类
# 需在treees.py顶部增加一行代码 import operator
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[:] # 复制类标签,存储在新列表subLabels中
myTree[bestFeatLabel][value]=createTree(splitDataSet\
(dataSet,bestFeat,value),subLabels)
return myTree
变量myTree包含了很多代表树结构信息的嵌套字典,从左边开始,第一个关键字no surfacing是第一个划分数据集的特征名称,该关键字也是另一个数据字典。对于其他关键词,其值可能是类标签,也可能是另一个数据字典。如果值是类标签,则该子节点就是叶子节点;如果值是另一个数据字典,则子节点是一个判断节点。
上述讲述了如何构造树,下面将绘制图形,方便描述数据信息的内在含义。