一、计算香农熵
香农熵的计算公式为
他表示样本集合D纯度的大小,取值越小,D的纯度越高。
下面是香农熵的计算公式的代码实现。
from math import log
import numpy as np
import operator
#计算熵
def ShannonEnt(DataSet): #计算信息熵已验证
m=len(DataSet)
classLabelList=[data[-1] for data in DataSet]
NumberDict={}
for classLabel in classLabelList:
NumberDict[classLabel]=NumberDict.get(classLabel,0)+1
ShannonEnt=0
for key in NumberDict.keys():
p=NumberDict[key]/m
ShannonEnt-=p*log(p,2)
return ShannonEnt
这里的基本想法是遍历所有样本的最后一个元素,即样本标记。统计样本每个类的数量,从而得到P。最后所有-p*log(p,2)
相加,得到香农熵。
二、根据属性及取值将数据集分开
下面我们要计算信息增益,以选择最佳属性aaa在决策树内部节点进行划分。
属性aaa的信息增益的计算公式为:
DvD^vDv为在属性aaa上属性取值为vvv的样本集。
信息增益越大,表示用属性aaa进行划分所获得的纯度提升越大。所以我们需要在划分过程中选取纯度提升最大属性。
在公式中由于出现DvD^vDv,所以要将DDD拆分,以获得DvD^vDv。下面为代码实现:
def splitDataSet(DataSet,axis,value):
splitDataList=[]
for data in DataSet:
if data[axis]==value:
leftData=data[:axis]
rightData=data[axis+1:]
leftData.extend(rightData)
splitDataList.append(leftData)
return splitDataList
这里我们使用的数据形式形如DataSet=[[1,1,'yes'],[1,1,'yes'],[1,0,'no']]
。
分割主要思路为将数据集分割为data[:axis]
和data[axis+1:]
,再将两个分割集结合,这是正好去除第axis
个属性,再用append
得到DvD^vDv。
这里我一开始使用del
删除axis
属性,后来发现出现问题,主要是因为使用del
删除属性,但是后面计算信息增益要遍历所有属性,使用del
就会对后面的属性信息增益的计算产生影响。
`三、计算信息增益
上面由于已经计算通过代码的到DvD^vDv,所以计算信息增益很容易,然后选择信息增益最大的属性作为划分属性。
以下为代码实现:
def ChooseBestFeature(DataSet):
CurrentInfoGain=0
m=len(DataSet)
d=len(DataSet[0])-1
BestFeature=-1
for axis in range(d):
totalSubEnt=0
ValueList=[data[axis] for data in DataSet]
ValueSet=set(ValueList)
for value in ValueSet:
split_data=splitDataSet(DataSet,axis,value)
p=len(split_data)/m
subEnt=p*ShannonEnt(split_data)
totalSubEnt+=subEnt
InfoGain=ShannonEnt(DataSet)-totalSubEnt
if InfoGain>CurrentInfoGain:
CurrentInfoGain=InfoGain
BestFeature=axis
return BestFeature
这里还可以使用其他准则,比如信息增益比,如果是CART决策树使用基尼系数。
四、建立决策树
一颗决策树的结构如图
每一层决策树选择一个最佳属性划分,每层选择一个属性后又得到一个样本集,又可以的到一个决策树,所以很自然想到用递归函数。递归终止条件为如果样本集中所有样本属于同一个类,将样本划分为这一类;如果只剩下一个属性则划分为占比最多的类。以下为代码实现:
def CreateTree(DataSet,labelsOfFeature):
ClassLabelList=[data[-1] for data in DataSet]
ClassLabelSet=set(ClassLabelList)
if len(ClassLabelSet)==1:
return DataSet[0][-1]
if len(DataSet[0])==1:
return mojority(DataSet)
Bestaxis=ChooseBestFeature(DataSet)
valueList=[data[Bestaxis] for data in DataSet]
valueSet=set(valueList)
BestFeature=labelsOfFeature[Bestaxis]
Tree={BestFeature:{}}
del labelsOfFeature[Bestaxis]
for value in valueSet:
labels=labelsOfFeature[:]
Tree[BestFeature][value]=\
CreateTree(splitDataSet(DataSet,Bestaxis,value),labels)
return Tree
这里的labelsOfFeature
表示每一列所代表的特征。
这里由于需要计算最大占比类,所以这里还编写一个momojority函数计算最大占比类,代码如下:
def mojority(DataSet):
classLabelList=[data[-1] for data in DataSet]
NumberDict={}
for classLabel in classLabelList:
NumberDict[classLabel]=NumberDict.get(classLabel,0)+1
sortNumberDict=sorted(NumberDict.items(),key=operator.itemgetter(1),reverse=True)
MostOfClass=sortNumberDict[0][0]
return MostOfClass
现在我们就穿件了一棵决策树,现在我们可以做个检验:
labels=['age',' spectacle prescription','astigmatic','tear production rate']
tree=[['young', 'myope', 'no', 'reduced', 'no lenses'], ['young', 'myope', 'no', 'normal', 'soft'], \
['young', 'myope', 'yes', 'reduced', 'no lenses'], ['young', 'myope', 'yes', 'normal', 'hard'], ['young', 'hyper', 'no', 'reduced', 'no lenses'], \
['young', 'hyper', 'no', 'normal', 'soft'], ['young', 'hyper', 'yes', 'reduced', 'no lenses'], ['young', 'hyper', 'yes', 'normal', 'hard'],\
['pre', 'myope', 'no', 'reduced', 'no lenses'], ['pre', 'myope', 'no', 'normal', 'soft'], ['pre', 'myope', 'yes', 'reduced', 'no lenses'], \
['pre', 'myope', 'yes', 'normal', 'hard'], ['pre', 'hyper', 'no', 'reduced', 'no lenses'], ['pre', 'hyper', 'no', 'normal', 'soft'], \
['pre', 'hyper', 'yes', 'reduced', 'no lenses'], ['pre', 'hyper', 'yes', 'normal', 'no lenses'], ['presbyopic', 'myope', 'no', 'reduced', 'no lenses'],\
['presbyopic', 'myope', 'no', 'normal', 'no lenses'], ['presbyopic', 'myope', 'yes', 'reduced', 'no lenses'], \
['presbyopic', 'myope', 'yes', 'normal', 'hard'], ['presbyopic', 'hyper', 'no', 'reduced', 'no lenses'], ['presbyopic', 'hyper', 'no', 'normal', 'soft'],\
['presbyopic', 'hyper', 'yes', 'reduced', 'no lenses'], ['presbyopic', 'hyper', 'yes', 'normal', 'no lenses']]
print(CreateTree(tree,labels))
得到:
{'tear production rate': {'normal': {'astigmatic':{'no': {'age': {'presbyopic': {' spectacle prescription':\
{'myope': 'no lenses', 'hyper': 'soft'}}, 'pre': 'soft', 'young': 'soft'}}, 'yes': {' spectacle prescription': \
{'myope': 'hard', 'hyper':{'age': {'presbyopic': 'no lenses', 'pre': 'no lenses', 'young': 'hard'}}}}}}, \
'reduced': 'no lenses'}}