决策树算法详解(2)

Python决策树原生版参考

#encoding:utf-8
import math

def createDataSet():
        #训练数据集
        dataSet=[['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']
                 ]
        #数据分为两类yes,no
        #labels为特征的名称
        labels=['age','prescript','Astigmatic','tearRate']
        return dataSet,labels

#计算给定数据集的熵
def calcShannonEnt(dataSet):
        #总的训练样本条数
        numEntries=len(dataSet)
        #类标签,每条样本所属类别
        labelCounts={}
        for featVec in dataSet:
            #每条最后一列为各自类别
            currentLabel=featVec[-1]
            #为所有可能的类别取值建立<key,value>结构
            #key表示类别,value表示该类出现次数
            #此处初始化
            if currentLabel not in labelCounts.keys():labelCounts[currentLabel]=0
            #出现一次加一次
            labelCounts[currentLabel]+=1
        #保存信息熵
        shannonEnt=0.0
        #样本遍历完后,计算各类别占总样本的,比例,即概率
        #遍历词典<key,value>结构
        for key in labelCounts:
            #计算该类别的比例
            prob=float(labelCounts[key])/numEntries
            #计算信息增益,以2为底数取对数
            shannonEnt-=prob*math.log(prob,2)
        #返回数据集熵
        return shannonEnt

#计算条件熵,划分数据集
def splitDataSet(dataSet,axis,value):
    #定义新变量,保存划分后的数据集
    retDataSet=[]
    #遍历数据集每一条数据
    for featVec in dataSet:
        #将符合要求的数据抽取出来存入retDataSet中
        if featVec[axis]==value:
            #除给定的特征axis及值value,整行数据被保存下来
            #如选取axis为年龄,value为青年
            #这部分数据在数据集中有多少行都会被保存下来
            #但年龄为青年这一列数据不会被保存
            reducedFeatVec=featVec[:axis]
            reducedFeatVec.extend(featVec[axis+1:])
            #保存去除该列后的数据
            retDataSet.append(reducedFeatVec)
    #返回去除指定特征值列的数据,便于计算该条件下的条件熵
    return retDataSet

#选择最好的特征划分数据集,返回最佳特征下标。
#最好的特征即为信息增益最大的特征
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]
        #当前特征下对应值去重,即每个特征值唯一
        #如年龄,取值3个青年、中年、老年
        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

#递归构建决策树
#多数表决的方法决定叶子节点的分类
#所有特征都用完时,以数据集中类别数量最多的类别作为最终类别
def majorityCnt(classList):
    #<key,value>数据集中每个类别出现的次数
    classCount={}
    #遍历数据集中的类别
    for vote in classList:
        #初始类别第一次加入字典最终
        if vote not in classCount.keys():classCount[vote]=0
        #记录次数
        classCount[vote]+=1
    #遍历结束后,次数value值从大到小进行排列
    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]

    #类别完全相同则不划分,返回类标签
    #具体所有数据值为同一个值,如classList[0]='yes'个数,
    #为整个列表长度,显然所有的值均为yes
    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[:]
        #以当前的特征值划分子集
        #以子集为参数,递归调用创建树的方法
        #将递归调用的结果作为树节点的一个分支
        myTree[bestFeatLabel][value]=createTree(splitDataSet(dataSet,bestFeat,value),subLabels)
    #返回树
    return myTree

#使用决策树进行分类
#输入:训练好的决策树
#构建树的类别标签向量(用于确定特征在数据集中的位置)
#测试数据
#判断测试数据testVec,属于哪个类别
def classify(inputTree,featLabels,testVec):
    #firstStr存放决策树的根节点名称
    #取得根节点名称no surfacing
    #firstStr='no surfacing'
    firstStr=inputTree.keys()[0]
    #secondDict值为除树根节点名称外的值
    #即{0:'no',1:{'filippers':{0:'no',1:'yes'}}}
    secondDict=inputTree[firstStr]
    #index方法查找当前列表中第一个匹配firstStr变量的元素的索引
    #即找到树根节点在所有特征列的第几列
    featIndex=featLabels.index(firstStr)

    #测试数据对应根节点下的取值
    key=testVec[featIndex]
    #secondDict[0]='no' secondDict[1]='{'flippers':{0:'no',1:'yes'}}'
    valueOfFeat=secondDict[key]
    #判断valueOfFeat的类型
    #valueOfFeat为dict词典类型,递归寻找
    if isinstance(valueOfFeat,dict):
        classLabel=classify(valueOfFeat,featLabels,testVec)
    #valueOfFeat为数值,直接返回该值
    #此处valueOfFeat=secondDict[key]='no' 返回no
    else:classLabel=valueOfFeat
    #返回最终类别
    return classLabel

#决策树的存储
#pickle序列化对象,可以在磁盘上保存对象
def storeTree(inputTree,filename):
    import pickle
    fw=open(filename,'w')
    pickle.dump(inputTree,fw)
    fw.close()

#并在需要的时候将其读取出来
def grabTree(filename):
    import pickle
    fr=open(filename)
    return pickle.load(fr)



myDat,labels=createDataSet()
myTree=createTree(myDat,labels)
#print myTree
storeTree(myTree,'1.txt')
Tree=grabTree('1.txt')
#print Tree
aaa=classify(Tree,['age','prescript','Astigmatic','tearRate'],['young','hypper','yes','normal'])
print aaa
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值