python实现决策树算法

决策树伪代码是:

  1.  检测数据集中的每个子项是否为同一个分类
  2. 如果是,则返回该类标签
  3. 如果不是:

                     根据信息增益寻找划分此数据集的最好的特征

                     划分数据集

                     创建分支节点

                                 for   每个划分的子集

                                           调用自身函数形成递归并增加返回结果到分支节点中

                      return   分支节点

★  必要知识

1.  香农熵公式:

 Ent(D)=-\sum_{k=1}^{|y|}p_{k}\cdot log_{2} \cdot p_{k}

其中: p_{k}}为样本数据集D中第K类样本所占的比例(k=1,2,...|y|), 在本例中,k类就是是否为鱼类,k要么是1,要么是2,就这两种情况,所以p{_{k}} 就是正例(是鱼类)或者反例(不是鱼类)的概率

2.  信息增益 :

Gain(D,a)=Ent(D)-\sum_{v=1}^{V}\frac{|D^{v}|}{|D|}Ent(D^{v})

     D数据集中,在属性(或特征)a上有V个不同的取值{a^{1},a^{2}, ... a^{V} }。因此用属性a对数据集D进行划分,会产生V个分支点,其中,第v个分支节点包含了D中所有在属性a上取值为 a^{v}的样本,记为 D^{v}

考虑到不同的分支点所包含的数据个数不同,所以给分支节点赋予权重 |D^v|/|D| ,故数据个数越多的分支节点影响越大

其python代码如下:

1.  得到数据集及特征属性:

海洋生物数据
 不浮出水面是否能生存(no surfacing)是否有脚蹼(flippers)属于鱼类
1                                是             是    yes
2                                是             是    yes
3                                是             否     no
4                                否             是     no
5                                否             是     no
# coding=utf-8
import operator                    #运算符
from math import log

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

2.  计算给定数据集的香农熵

def calcShannonEnt(dataSet):       #dataSet是输入的列表
    numEntries=len(dataSet)        #得到数据集的行数
    labelCount={}                  #labelCount是存放类别标签及其对应个数的字典
    for featVec in dataSet:        #遍历数据集,得到的是一个个一维列表
        currLabel=featVec[-1]      #currLabel是featVec的最后一列,结合咱们这个数据集的特点,最后一列是类别标签(判断是不是鱼类),所以currLabel不是yes就是no,并且currLabel是个一列表
        if currLabel not in labelCount.keys():   #如果这个类别标签(yes或者no)不在labelCount字典的键中
            labelCount[currLabel]=0   #那么把这个标签存入labelCount的"键"中,并把其对应的"值"赋为0
        labelCount[currLabel]+=1     #如果在的话把该类别标签对应的“值”加上1
    shannonEnt=0.0             #初始香农熵为0
    for key in labelCount:          #遍历字典,得到的是字典的“键”
        prob=float(labelCount[key])/numEntries     #算出该类别标签(yes或no)的概率,即公式中的p(x),其实就是计算正例的概率和反例的概率
        shannonEnt -= prob * log(prob, 2)  # 根据数学公式写出的香农熵代码,log函数表示以2为底,prob的对数
    return shannonEnt                # 返回香农熵

3.  根据选定的特征划分数据集(选出既满足划分的又把该特征剔除的剩余的数据集存贮下来)

def splitDataSet(dataSet,axis,value):    #axis表示你要选取数据集中的第axis列作为特征,value为你取的值,用它和刚选取的第axis列的特征值做比较
    retDataSet=[]          #保存提出特征之后的剩余的数据集,先设成列表,这样可以用append和extend函数
    for featVec in dataSet:     #遍历数据集,取出featVec的是一个列表
        if featVec[axis]==value :   #选择划分依据,即此列满足选择要求,可作为特征提出去
           reduceFeatVec=featVec[:axis]  #把aixs之前的列拷贝到reduceFeatVec列表中,当然不包括第axis列,遵循左闭右开原则
           reduceFeatVec.extend(featVec[axis+1:])  #把从第axis+1列开始到最后一列添加到reduceFeatVec中
           retDataSet.append(reduceFeatVec)      #以满足划分条件为前提,把每一个子项的特征提出去之后,再一个个存入retDataSet二维列表中,形式基本是[[x,e,w],[w,r,n],[s,e,t]...]
    return retDataSet          #返回剩余的数据集

注意: extend()  是只把内容添加进去,不包括内容以前的格式

  而      append()  把内容和格式一起添加进去了

4.  选择出最好的数据集划分:(即信息增益越大,划分后的纯度越高,越能符合我们的要求)

def chooseBestFeatureToSplit(dataSet):       #选择最好的特征
    numFeature=len(dataSet[0])-1             #得到每个子项的列数的下标值,因为我们的下标都是从0开始数的
    initEntry=calcShannonEnt(dataSet)       #初始香农熵赋为initEntry
    bestInfoGain=0.0                         #初始化最好的特征信息增益为0.0
    bestFeature=-1                           #初始化最好特征的下标为-1
    for i in range(numFeature):              #根据左闭右开,这里i是取不到numFeature的,即又少了最后一列,因为最后一列不是特征,是类别标签(即是不是鱼),所以在此不应该包含
        featList=[example[i] for example in dataSet]   #这里是列表生成式,example是数据集的每个子项(比如第一行),是列表格式,而examploe[i]为某一行的第i个元素,它是某个特征的对应值,则featList是包含这些特征值的列表,相当于把数据集的特征值一列一列的提取出来了
        uniqueVals=set(featList)             #把这些特征值去重复
        newEntry=0.0                         #新的香农熵初始化为0
        for value in uniqueVals:             #遍历uniqueVals集合的值,里面是某个特征对应的值,比如本例中,若选取的是有无脚蹼特征,那么集合里面就是有脚蹼或者无脚蹼,即里面就是0和1
            subDataSet=splitDataSet(dataSet,i,value)    #选取此特征,subDataSet为满足该划分的数据集
            prob=len(subDataSet)/float(len(dataSet))    #满足该划分的数据集,prob就是公式中的权重
            newEntry+=prob*calcShannonEnt(subDataSet)   #计算该特征划分下不同特征取值的权重香农熵
        infoGain=initEntry-newEntry          #这就是信息增益公式
        if infoGain>bestInfoGain:            #挑选出信息增益最大的特征(属性)
            bestInfoGain=infoGain
            bestFeature= i
    return bestFeature                      #返回该特征的下标

5.  当数据集处理了所有属性,却没法确定最终类标签(此例中即到底是不是鱼),那么可通过多数表决的方法来确定最终分类

def majorityCnt(classList):        #classList为类标签,即是鱼(yes)或不是鱼(no)
    classCount={}                  #存放“键”为yes或no的字典,比较其对应的”值”便可知道谁出现的次数多,最终目的就是这个
    for vote in classList:
        if vote not in classCount.keys():  #判断类别(yes或no)是否为字典的“键“
           classCount[vote]=0             #如果不在,把yes或者no存入键内,对应的”值“初始化为0
        classCount[vote]+=1                #如果在,键对应的“值”加1,即计数加上1
    sortedClassCount=sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)  #把字典分割成一个个元组,并按每个元组的第二元素的大小从大到小排列
    return sortedClassCount[0][0]         #返回出现次数最多的类别的名称,即yes或no

6.  创建树:

def createTree(dataSet,labels):                       #labels保存的是特征的名称(比如本例的no surfacing,flippers)
    classList=[example[-1] for example in dataSet]    #把最后一列即类别(是否为鱼)存入到classList列表中
    if classList.count(classList[0])==len(classList):  #如果classList中的所有类标签都相同
       return classList[0]
    if len(dataSet[0])==1:                            #如果用完了所有特征
       return majorityCnt(classList)
    bestFeatSub=chooseBestFeatureToSplit(dataSet)      #得到最好特征的下标
    bestFeatLabel=labels[bestFeatSub]               #根据下标把特征名取出来
    myTree={bestFeatLabel:{}}                          #定义树的格式是字典格式
    del(labels[bestFeatSub])                          #从labels列表中删除变量名,但不删除它引用的数据
    featValues=[example[bestFeatSub] for example in dataSet]  #把该特征的取值取出来,放到featValues列表中
    uniqueValues=set(featValues)                        #把属性值去重复
    for value in uniqueValues:                          #遍历属性值
        subLabel=labels[:]                              #当函数的参数是列表时,参数是按引用方式传递的,为了保证每次调用函数createTree时不改变原列表的内容,故用sublabel代替原列表
        myTree[bestFeatLabel][value]=createTree(splitDataSet(dataSet,bestFeatSub,value),subLabel)    #递归调用
    return myTree

7.  实例化即函数调用

myDat,labels=createDataSet()
result=createTree(myDat,labels)
print(result)              #打印树,其实是字典格式

8.   输出结果:

C:\Users\Anaconda3\python.exe C:/Users/PycharmProjects/machine_learning/快速数据分析/测试.py
{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}

Process finished with exit code 0

                                                     

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值