决策树02——决策树的构建

来源:互联网 发布:淘宝店主进货渠道 编辑:程序博客网 时间:2024/05/29 15:58

《 machine learning in action》之决策树

计算给定数据集的香农熵

创建文件trees.py

# -*- coding=utf-8 -*-#计算给定数据集的熵from math import logdef calcShannonEnt(dataSet):    numEntries = len(dataSet)    labelCounts = {}  #实例总数    for featVec in dataSet:        #为所有可能分类创建字典        currentLabel = featVec[-1]       #将最后一列,即分类结果存入currentLabel        if currentLabel not in labelCounts.keys():       #若分类结果已经在labelCounts这个字典中            labelCounts[currentLabel] = 0              #若当前label不存在,则扩展字典,加入此键值        labelCounts[currentLabel] += 1                 #字典中的每个键值都记录了当前类别的数量    #print 'labelCounts,currentLabel:',labelCounts,currentLabel    #print 'labelCounts.keys()',labelCounts.keys()    #print 'labelCounts.values()',labelCounts.values()    shannonEnt = 0.0    for key in labelCounts:        prob = float(labelCounts[key])/numEntries        shannonEnt -= prob*log(prob,2)  #求以2为底的对数,其和即为熵        #print 'labelCounts[key]',labelCounts[key]    return shannonEnt

创建或导入dataSet

#鱼类数据集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

在命令提示符下输入:

In[9]: import treesIn[10]: myDat,labels = trees.createDataSet()In[11]: myDatOut[11]: [[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]In[12]: labelsOut[12]: ['no surfacing', 'flippers']In[13]: trees.calcShannonEnt(myDat)#混合的数据越多,熵越高,测试:In[18]: myDat[0][-1] = 'maybe'In[19]: myDatOut[19]: [[1, 1, 'maybe'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]In[20]: trees.calcShannonEnt(myDat)Out[20]: 1.3709505944546687

划分数据集

取出符合要求某个特征属性值的样本,并将其此特征从数据集中去除。

#按照给定特征划分数据集def splitDataSet(dataSet,axis,value):  #待划分的数据集,划分数据集的特征(第几列),需要返回的特征的值,(axis-->a,value-->v)    #为了不修改原始数据集,创建新的list对象    retDataSet = []    for featVec in dataSet:        #将符合特征的数据抽取出来        if featVec[axis] == value:  #Python中的数据.即featVec中的数据从第0列开始,[0,1,2,3,4,5,……]            reducedFeatVec = featVec[:axis] #[:axis]表示前axis行,若axis为3,则表示取festVec的前3列,即第[0,1,2]列            reducedFeatVec.extend(featVec[axis+1:])    #[axis+1:] 表示跳过axis+1列,从下一列数据取到最后一列的数据,即跳过第[3]列,从第[4]列到最后一列            retDataSet.append(reducedFeatVec)    return retDataSet

在Python命令行中输入:

In[16]: reload(trees)Out[16]: <module 'trees' from '/home/vickyleexy/PycharmProjects/Classification of contact lenses/trees.py'>In[17]: myDat,labels = trees.createDataSet()In[18]: myDatOut[18]: [[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]In[19]: trees.splitDataSet(myDat,0,1) #抽取出数据集中第0列中数据值为1的样本,并将此列剔除,组成新的样本集Out[19]: [[1, 'yes'], [1, 'yes'], [0, 'no']]In[20]: trees.splitDataSet(myDat,0,0) #抽取出数据集中第0列中数据值为0的样本,并将此列剔除,组成新的样本集Out[20]: [[1, 'no'], [1, 'no']]In[21]: trees.splitDataSet(myDat,1,0) #抽取出数据集中第1列中数据值为0的样本,并将此列剔除,组成新的样本集Out[21]: [[1, 'no']]

选取最好的数据集划分方式

即选取取出信息增益最大的特征。信息增益是熵减少或者说信息无序度的减少,信息增益越大,信息无序度减少越大,信息的可确定性越强。

#选择最好的数据集划分数据,即计算每种特征信息熵 Gain(D,a)def chooseBestFeatureToSplit(dataSet):    numFeatures = len(dataSet[0]) - 1                     #可选特征的个数    baseEntropy = calcShannonEnt(dataSet)  # 计算总体信息熵Ent(D)    bestInfoGain = 0.0;       #初始化最好的信息增益    bestFeature = -1;        #初始化选取的最好的特征    #创建唯一的分类标签列表    for i in range(numFeatures):           #遍历各个特征        featList = [example[i] for example in dataSet]   #将第i列特征的值选取出来,并存入featList        uniqueVals = set(featList)         #将featList存为集合的格式,即去除featList中重复的元素,因此uniqueVals中为每个特征的中不同的属性        newEntropy = 0.0        #计算每种划分方式的信息熵        for value in uniqueVals:        #遍历每个特征中的各个属性            subDataSet = splitDataSet(dataSet,i,value)       #选取符合条件的特征,并将此特征从样本中去除,以便进行下面的进一步计算            prob = len(subDataSet)/float(len(dataSet))       #符合此特征中此属性的个数占总体样本的比例,即|D^v|/|D|            newEntropy += prob * calcShannonEnt(subDataSet)  #计算各个特征中每个属性的加权信息熵的和        infoGain = baseEntropy - newEntropy               #计算信息增益,即Gain(D,a)        #计算最好额信息增益        if (infoGain > bestInfoGain):            bestInfoGain = infoGain            bestFeature = i    print '最好的特征,最好的信息增益:',bestFeature,bestInfoGain    return bestFeature

在Python命令行下输入以下代码进行测试:

In[9]: reload(trees)Out[9]: <module 'trees' from '/home/vickyleexy/PycharmProjects/Classification of contact lenses/trees.py'>In[10]: myDat,labels = trees.createDataSet()In[11]: myDatOut[11]: [[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]In[12]: trees.chooseBestFeatureToSplit(myDat)最好的特征,最好的信息增益: 0 , 0.419973094022Out[12]: 0

递归构建决策树

递归结束的条件:

  • 程序遍历完所有划分数据集的属性
  • 每个分支下的所有实例都具有相同的分类

若已经处理了所有的属性,但类标签依然不是唯一的,此时采用多数表决的方法决定叶子节点的分类。
在trees.py文件的顶部添加operator库

import operator

统计剩余样本中属于哪一类别最多的数量最多:

def majorityCnt(classList):     #classList类别列表    classCount = {}             #新建立一个字典    for vote in classList:       #遍历所有的类别        #统计各类别剩余样本的数量        if vote not in classCount.keys():     #如果此类别不在classCount的key中            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):  #若classList中第一个类别的数量等于样本的总数量,即样本类别完全相同        return classList[0]    #遍历完所有特征时返回出现次数最多的    if len(dataSet[0]) == 1:        return majorityCnt(classList)    bestFeat = chooseBestFeatureToSplit(dataSet)    bestFeatLabel = labels[bestFeat]    myTree = {bestFeatLabel:{}}    del(labels[bestFeat]) #去掉已经使用的(bestFeat)类标签    #得到最好的特征这一列,包含其所有属性值的列表    featValues = [example[bestFeat] for example in dataSet]    uniqueVals = set(featValues)        #去掉featValues列表中重复的属性    for value in uniqueVals:             #遍历最好的特征中所有的属性        subLabels = labels[:]            #复制del(labels[bestFeat])后的结果,并将其存在新列表变量subLabels中        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet,bestFeat,value),subLabels)    return myTree

命令行中输入:

In[66]: reload(trees)Out[66]: <module 'trees' from '/home/vickyleexy/PycharmProjects/Classification of contact lenses/trees.py'>In[67]: myDat,labels = trees.createDataSet()In[68]: myTree = trees.createTree(myDat,labels)最好的特征,最好的信息增益: 0 , 0.419973094022最好的特征,最好的信息增益: 0 , 0.918295834054In[69]: myTreeOut[69]: {'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}