##一.创建分支的伪代码函数createBranch()如下所示: ##
If so return 类标签:
Else
寻找划分数据集的最好特征
创分数据集
创建分支节点
for 每个划分的子集
调用函数createBranch()并增加返回结果到分支节点中
return 分支节点
**其实createBranch是一个递归函数,在倒数第二行调用了自己**
二.信息论——信息增益
划分数据集的大原则是:将无序的数据变得更加有序。我们可以使用多种方法划分数据集。组织杂乱无章数据的一种方法就是使用信息论度量信息,信息论是量化处理信息的分支科学。我们可以在划分数据之前使用信息论量化度量信息的内容。
在划分数据集之前之后信息发生的变化称为信息增益,计算每个特征值划分数据集获得的信息增益,获得信息增益最高的特征就是最好的选择。 (划分的依据是取得获得信息增益最高的)
熵定义为信息的期望值,那么信息的定义又是什么:如果待分类的事务可能划分在多个分类之中,则信息的定义:x_{i}=-log_{2}p(x_{i}) 这里的 p(x_{i}) 指的是选择该类的概率
则熵:H=-\sum_{i=1}^{n}p(x_{i})log_{2}p(x_{i}),其中n代表的是选择的类别,即类别的总数
1.计算香农熵:
from math import log #导入对数函数
def shanNonEnt(dataset):
numentries=len(dataset)
labelcounts={} #创建一个空字典
for featvec in dataset: #dataset是列表
currenlabel=featvec[-1] #将featvec的最后一个元素提取
if currenlabel not in labelcounts.keys():
labelcounts[currenlabel]=1 #若此时的label在labelcounts没有,则赋值为1
else:
labelcounts[currenlabel] +=1 #如果有,则加1
shannonEnt=0.0
for key in labelcounts:
prob=float(labelcounts[key])/numentries #计算概率
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']]
return dataset
结果输出为:
>>> import tree
>>> reload(tree)
<module 'tree' from 'tree.pyc'>
>>> dataset=tree.createdataset()
>>> tree.shanNonEnt(dataset)
0.9709505944546686
如果我们不断的向产生的数据集里添加标签,情况会是怎么样呢,如下:
def createdataset():
dataset=[[1,1,'yes'],[1,1,'yes'],[1,0,'no'],[0,1,'no'],[0,1,'no'],[2,1,'maybe']] #多添加一个类别
return dataset
output:1.4591479170272448
def createdataset():
dataset=[[1,1,'yes'],[1,1,'yes'],[1,0,'no'],[0,1,'no'],[0,1,'no'],[2,1,'maybe'],[1,0,'haha']]
return dataset
output:1.8423709931771086
从上述可以看到,当数据集中的类别越多的时候,香农熵越来越大
2.划分数据集
分类算法除了需要测量信息熵,还需要划分数据集,度量划分数据集的熵,以便判断是否正确划分了数据集。我们将对每个特征划分数据集的结果计算一次信息熵,然后判断按照哪个特征划分数据集是最好的划分方式。程序如下:
def splitDataSet(dataset,axis,value):#待划分的数据集,划分数据集的特征、特征的返回值
retDataSet=[]
for featVec in dataset: #取出一组数据的所有特征,是一个列表
if featVec[axis]==value: #当选中的那一个特征刚好等于我们指定的特征的时候
reducedFeatVec=featVec[:axis] #对axis前面的元素赋值给reducedFeatVec
reducedFeatVec.extend(featVec[axis+1:]) #将axis后面的元素赋值给reducedFeatVec
retDataSet.append(reducedFeatVec)
return retDataSet
输出:
>>> import tree
>>> reload(tree)
<module 'tree' from 'tree.pyc'>
>>> dataset=tree.createdataset()
>>> tree.splitDataSet(dataset,1,1)
[[1, 'yes'], [1, 'yes'], [0, 'no'], [0, 'no'], [2, 'maybe']]
在上述的例子中,我们需要注意的是list.append 和 list.extend这个俩个函数的区别:尽管在大多数情况下这俩个函数具有很相似的特征,但是对于俩个列表而言,却是不一样的:
>>> a=[1,2,3]
>>> b=[2,3,4]
>>> a.extend(b) #将列表b的元素拆开,逐个赋值给a
>>> a
[1, 2, 3, 2, 3, 4]
>>> a=[1,2,3]
>>> b=[2,3,4]
>>> a.append(b)
>>> a
[1, 2, 3, [2, 3, 4]] #这里是将列表b当做一个元素赋值到a中
3.循环计算香农熵和splitDataSet()函数
def chooseBestFeatureToSplit(dataset):
numfeature=len(dataset[0])-1 #所有特征类别的数目
baseEntropy=shanNonEnt(dataset) #计算香农熵,这里计算的香农熵是用最后一个标签来计算的
bestinfoGain=0.0;bestfeature=-1
for i in range(numfeature): #循环所有的特征
featList=[example[i] for example in dataset] #将同一类特征的所有取值放到一个list表中
uniqueval=set(featList)
newEntropy=0.0
for value in uniqueval: #对于同一类特征中的不同取值
subdataset=splitDataSet(dataset,i,value)
pro=len(subdataset)/float(len(dataset)) #取得该特征的概率
newEntropy +=pro*shanNonEnt(subdataset) #算出按照这个特征分类的香农熵
infoGain=baseEntropy-newEntropy #算出信息增益,香农熵的变化
if(infoGain>bestInforGain): #比较不同特征算出来的信息增益,信息增益大的较好,即为要求的特征
bestinfoGain=infoGain
bestFeature=i
return bestFeature