决策树算法python实现

决策树算法python实现

01.前言

决策树的特点
优点:计算复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可以处理不相关特征数据。
缺点:可能会产生过度匹配问题。
适用数据类型:数值类型和标称型。
当然决策树算法,从最初的id3发展到现在id4.5,其中的主要区别是在对信息度量方式的不同。这里主要依据ID3算法去划分数据集。
除此之外,我们还需要知道的知识就是信息增益。在划分数据集之前和之后,信息发生的变化称为信息增益,这时信息增益最高的特征就是最好的选则。这时我们就需要知道香农的信息论的一些知识–熵。这里可以参见自然语言处理中数学基础(信息论)
程序思想
这里写图片描述
使用python计算信息熵

#导包
from math import log
#计算熵的函数
def calcShannonEnt(dataSet):
    #获取数据集的长度
    numEntries=len(dataSet)
    #定义一个字典,统计一个标签及其数量
    labelCounts={}
    #遍历数据集
    for featVec in 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)
    return shannonEnt
#测试主函数
def main():
    dataSet=[[1,1,'yes'],[1,0,'yes'],[1,0,'no'],[0,1,'no'],[0,1,'no']]
    print(calcShannonEnt(dataSet))
if __name__ == '__main__':
    main()

02.划分数据集

按照指定特征划分数据集,代码如下:

#参数说明:dataSet:待划分的数据集,axis:划分数据集的特征(索引),value:需要返回的特征的值
def splitDataSet(dataSet,axis,value):
    #定义一个列表,存放划分好了的数据集
    retDataSet=[]
    #遍历数据集
    for featVec in dataSet:
        #判断一条数据是否是需要返回的值
        if featVec[axis]==value:
            #截取列表从索引为0到索引为axis
            reducedFeatVec=featVec[:axis]
            #将原来列表中索引为axis+1到结束放到截取的新列表中
            reducedFeatVec.extend(featVec[axis+1:]) 
            #将整个结果集放到要分离出特征的数据集中
            retDataSet.append(reducedFeatVec)
    return retDataSet   

需要说明的是,这个函数的作用是:对数据集dataSet数据进行划分,取出数据集中索引为axis,标签值为value的所有数据。
这里还用使用了extend方法和append()方法,其中的区别可以参见python 列表自带extend()、append()的区别
通过比较信息增益(可参考信息增益的计算)去找出最佳的数据集划分方式:

#选则最好的数据集划分方式
def chooseBestFeatureToSplit(dataSet):
    #获取数据集的特征数,出去标签
    numFeatures=len(dataSet[0])-1
    #计算数据集的信息熵
    baseEntropy=calcShannonEnt(dataSet)
    #定义最佳信息增益
    bestInfoGain=0.0
    bestFeature=-1  #先默认为最后一个
    for i in range(numFeatures):
        #将所有数据集中的第i个特征放到一个list中
        featList=[example[i] for example in dataSet]
        #去重(获取数据集中有的对应特征有几个结果)
        uniqueVals=set(featList)
        newEntropy=0.0
        #遍历,构建子数据集
        for value in uniqueVals:
            #将数据集中的子数据,去掉索引为i的特征,并且做一个匹配,返回一个只有vaule值得数据集
            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

如果python中Set集合不清楚的可以查看python set()集合的使用

03.使用递归构建决策树

在此之前,我们需要清楚一点,如果在最后一个特征的时候,依然划分不出来的使用,这里我们就需要采取办法解决,就是在该特征情况下,取情况出现最多的那一个结果:

def majorityCnt(classList):
    classCount={}
    #遍历集合
    for vote in classList:
        if vote not in classCount.keys():classCount[vote]=0
        classCount[vote]+=1
    #利用operator操作键值排序
    sortedClassCount=sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
    return sortedClassCount[0][0]

这里需要导入:import operator
对于operator.itemgetter()使用不清楚的可以查见:python operator.itemgetter()函数的使用
创建决策树的函数:

#创建树的函数
def createTree(dataSet,labels):
    #获取数据集的标签(最后一列数据)
    classList=[example[-1] for example in dataSet]
    #递归的第一个停止条件
    #判断:如果数据集中一个标签的数量与这个标签集长度相同,就返回标签
    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]
    #使用set集合去除重复的值
    uniqueVals=set(featValues)
    #遍历所有最佳划分的分类标签
    for value in uniqueVals:
        #复制类标签
        subLabels=labels[:]
        #递归,向决策树字典添加分类信息,返回的结果就是一个字典
        myTree[bestFeatLabel][value]=createTree(splitDataSet(dataSet,bestFeat,value), subLabels)
    #返回决策树
    return myTree

这里需要注意的是递归函数一定要有出口。

04.程序运行结果

创建一个测试函数:

#测试主函数
def main():
    #数据集说明:yes为属于鱼类,no不属于鱼类
    dataSet=[[1,1,'yes'],[1,0,'yes'],[1,0,'no'],[0,1,'no'],[0,1,'no']]
    #对应特征名
    labels=['no surfacing','flippers']
    result=splitDataSet(dataSet, 0, 1)
    result2=chooseBestFeatureToSplit(dataSet)
    #生成树
    result3=createTree(dataSet, labels)

    print("计算数据集的熵:"+str(calcShannonEnt(dataSet)))
    print("根据分离出单独数据集:"+str(result))
    print("数据集最佳根:"+str(result2))
    print("数据集最佳根:"+str(result3))

if __name__ == '__main__':
    main()

程序运行结果:

{'flippers': {0: 'yes', 1: 'yes'}}
{'no surfacing': {0: 'no', 1: {'flippers': {0: 'yes', 1: 'yes'}}}}
计算数据集的熵:0.9709505944546686
根据分离出单独数据集:[[1, 'yes'], [0, 'yes'], [0, 'no']]
数据集最佳根:0
数据集最佳根:{'no surfacing': {0: 'no', 1: {'flippers': {0: 'yes', 1: 'yes'}}}}
[Finished in 0.3s]

写在后面

如果对机器学习,深度学习,自然语言处理等感兴趣的,可以关注博主的个人订阅号:
这里写图片描述

猜你喜欢

转载自blog.csdn.net/meiqi0538/article/details/80444105