决策树的基础部分还是很简单易懂的,但是剪枝处理和多决策问题我现在还没有接触到。我也是个初学者,从初学者角度写的,大佬就绕道了吧。
我们决策树的划分都是建立在熵的基础之上的,首先是熵的计算公式:
(其中k是我们的种类编号)
然后还有一个很重要的概念--信息增益:
其中v表示a这种属性的第v个类别,其中信息增益越大越好,最大的做为我们的划分属性。
不需要划分的三种情况:
(1)当前节点包含的样本全属于同一类别;
(2)当前属性集为空,或是所有样本在所有属性上取值相同,无法划分;
(3)当前节点包含的样本集合为空,不能划分
注意(2)(3)的区别,(2)是把当前节点标记为叶节点,并将其类别设定为该节点所含样本最多的类别,(3)同意把当前节点标记为叶节点,但将其类别设定为其父节点所含样本最多的类别。这两种清醒的处理实质不同,(2)是利用当前节点的后验分布,(3)把父节点的样本分布作为当前节点的先验分布。
之后我们就基于这两个公式开始我们的决策
从网上找了一组数据来做为样例:
然后我们就使用上面的数据,并选择活动这列做为我们的划分目标,那么我们就开始第一步 计算我们的信息熵:
def calcShannonEnt(dataSet):#计算信息熵
numEntries = len(dataSet)
labelCounts = {}
for featVec in dataSet: #读取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
shannonEnt -= prob*log(prob,2) #导入math库
return shannonEnt
整个实现过程就是按照公式一步一步写出来的,不懂得可以留言哈
然后对每个属性都计算他们的信息增益,将最大的信息增益作为我们的划分属性,求解每个属性的信息增益按照我们上面的公式计算。
#分成两部分第一个函数splitdata是负责把我们需要的属性类别专门提取出来,就比如先算天气中的晴天,就把晴天那几行挑选出来
#第二个函数是负责挑选属性及属性值分别放到splitdata当中来计算。
def splitDataSet(dataSet,axis,value):
retDataSet = []
for featVec in dataSet:#取大列表中的每个小列表
if featVec[axis]==value:
reduceFeatVec=featVec[:axis]
reduceFeatVec.extend(featVec[axis+1:])
retDataSet.append(reduceFeatVec)
return retDataSet #返回不含划分特征的子集
def chooseBestFeatureToSplit(dataSet):
numFeature = len(dataSet[0]) - 1
baseEntropy = calcShannonEnt(dataSet)
bestInforGain = 0
bestFeature = -1
for i in range(numFeature):
featList = [number[i] for number in dataSet]#得到某个属性下所有值(某列)
uniquelVals = set(featList) #set无重复的属性特征值,得到所有无重复的属性取值
#计算每个属性i的概论熵
newEntropy = 0
for value in uniquelVals:
subDataSet = splitDataSet(dataSet,i,value)#得到i属性下取i属性为value时的集合
prob = len(subDataSet)/float(len(dataSet))#每个属性取值为value时所占比重
newEntropy+= prob*calcShannonEnt(subDataSet)
inforGain = baseEntropy - newEntropy #当前属性i的信息增益
if inforGain>bestInforGain:
bestInforGain = inforGain
bestFeature = i
return bestFeature#返回最大信息增益属性下标
上面这个函数大致的意思是:比如第一次先选择天气,然后把天气中的类别晴、阴、雨放在一个集合当中,然后把他们分别拿出来,第一次拿晴天,把晴天这个值带入到splitdata当中,把晴天对应的几行数据挑出来,然后计算信息增益的。
之后把最大的信息增益作为我们的划分属性,然后在划分属性的基础之上,在寻找最大增益。
之后就要构造决策树了:
#递归创建树,用于找出出现次数最多的分类名称
def majorityCnt(classList):
classCount={}
for vote in classList:#统计当前划分下每中情况的个数
if vote not in classCount.keys():
classCount[vote]=0
classCount[vote]+=1
sortedClassCount=sorted(classCount.items,key=operator.itemgetter(1),reverse=True)
#classcount.items 返回的是(key,value)组成的列表,key=operator.itemgetter(1),是一个临时函数,访问第()域,这里访问的就是value,按照value的大小来排序,注意导入包operator
return sortedClassCount[0][0]
def createTree(dataSet,labels):
classList=[example[-1] for example in dataSet]#创建数组存放所有标签值,取dataSet里最后一列(结果)
#类别相同,停止划分
if classList.count(classList[-1])==len(classList):#判断classList里是否全是一类,count() 方法用于统计某个元素在列表中出现的次数
return classList[-1] #当全是一类时停止分割
#长度为1,返回出现次数最多的类别
if len(classList[0])==1: #当没有更多特征时停止分割,即分到最后一个特征也没有把数据完全分开,就返回多数的那个结果
return majorityCnt(classList)
#按照信息增益最高选取分类特征属性
bestFeat=chooseBestFeatureToSplit(dataSet)#返回分类的特征序号,按照最大熵原则进行分类
bestFeatLable=labels[bestFeat] #该特征的label, #存储分类特征的标签
myTree={bestFeatLable:{}} #构建树的字典
del(labels[bestFeat]) #从labels的list中删除该label
featValues=[example[bestFeat] for example in dataSet]
uniqueVals=set(featValues)
for value in uniqueVals:
subLables=labels[:] #子集合 ,将labels赋给sublabels,此时的labels已经删掉了用于分类的特征的标签
#构建数据的子集合,并进行递归
myTree[bestFeatLable][value]=createTree(splitDataSet(dataSet,bestFeat,value),subLables)
return myTree