【机器学习实战系列】读书笔记之DecisionTree(ID3算法)(一)

本文介绍决策树ID3(Iterative Dichotomiser)算法,除此之外决策树还有C4.5,CART(Classification And Regression Tree)算法。

一、ID3算法介绍:

1.ID3算法是以信息熵和信息增益作为衡量标准的分类算法。

2.ID3算法是决策树的一种,它是基于奥卡姆剃刀原理的,即用尽量用较少的东西做更多的事。ID3算法

   即Iterative Dichotomiser 3迭代二叉树3代,是Ross Quinlan发明的一种决策树算法,这个

   算法的基础就是上面提到的奥卡姆剃刀原理,越是小型的决策树越优于大的决策树,尽管如此,也不总

   是生成最小的树型结构,而是一个启发式算法。

3.ID3算法的核心思想就是以信息增益来度量属性的选择,选择分裂后信息增益最大的属性进行分裂,该算法采用自顶向下的贪婪搜索遍历可能的决策空间。

4.ID3算法只适用于标称型数据,不适用于数值型数据。

  补充:

监督学习一般使用两种类型的目标变量:标称型和数值型

标称型:标称型目标变量的结果只在有限目标集中取值,如真与假(标称型目标变量主要用于分类)

数值型:数值型目标变量则可以从无限的数值集合中取值,如0.100,42.001等 (数值型目标变量主要用于回归分析)

二、决策树介绍:

决策树的优点:计算复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可以处理不相关特征数据。

决策树的缺点:可能会产生过度匹配问题。

适用数据类型:数值型和标称型

三、ID3算法工作原理:

1.得到原始数据集

2.基于最好的属性(特征)划分数据集

3.特征值>2,递归划分,递归结束的条件:程序遍历完所有划分数据集的属性,或者每个分支下的所有实例都具有相同的分类。

四、ID3算法的python代码实现:

#导入log运算符
from math import log
import operator

#计算给定数据集的熵
def calEnt(dataSet):
    #获取数据集的行数
    numEntries=len(dataSet)
    #设置字典的数据结构
    labelCounts={}
    #提取数据集的每一行的特征向量
    for featVec in dataSet:
        #获取特征向量的最后一列的标签
        currentLabel=featVec[-1]
        #检测字典的关键字key中是否存在该标签
        #如果不存在keys()关键字
        if currentLabel not in labelCounts.keys():
            #将当前标签/0键值对存入字典中
            labelCounts[currentLabel]=0
        #否则将当前标签对应的键值加1
        labelCounts[currentLabel]+=1
    #初始化熵为0
    Ent=0.0
    #对于数据集中所有的分类类别
    for key in labelCounts:
        #计算各个类别出现的频率
        prob=float(labelCounts[key])/numEntries
        #计算各个类别信息期望值
        Ent-=prob*log(prob,2)
    #返回熵
    return Ent

#创建一个简单的数据集
#数据集中包含两个特征'no surfacing','flippers';
#数据的类标签有两个'yes','no'
def creatDataSet():
    dataSet = [[1, 1, 'yes'],  #数据集的最后一列是类别标签
               [1, 1, 'yes'],
               [1, 0, 'no'],
               [0, 1, 'no'],
               [0, 1, 'no']]
    labels = ['no surfacing', 'flippers']  #lables是属性,也可以叫做特征
    # 下面数据为web数据挖掘这本书上决策树这一章节的数据
    '''dataSet=[ ['young', False, False, 'fair', 'No'],
            ['young', False, False, 'good', 'No'],
            ['young', True, False, 'good', 'Yes'],
            ['young', True, True, 'fair', 'Yes'],
            ['young', False, False, 'fair', 'No'],
            ['middle', False, False, 'fair', 'No'],
            ['middle', False, False, 'good', 'No'],
            ['middle', True, True, 'good', 'Yes'],
            ['middle', False, True, 'excellent', 'Yes'],
            ['middle', False, True, 'excellent', 'Yes'],
            ['old', False, True, 'excellent', 'Yes'],
            ['old', False, True, 'good', 'Yes'],
            ['old', True, False, 'good', 'Yes'],
            ['old', True, False, 'excellent', 'Yes'],
            ['old', False, False, 'fair', 'No']]
    labels=['Age', 'Has_job', 'Own_house', 'Credit_rating']'''
    #返回数据集和类标签
    return dataSet,labels

#划分数据集:按照最优特征划分数据集
#@dataSet:待划分的数据集
#@axis:划分数据集的特征
#@value:特征的取值
def splitDataSet(dataSet,axis,value):
    #需要说明的是,python语言传递参数列表时,传递的是列表的引用
    #如果在函数内部对列表对象进行修改,将会导致列表发生变化,为了
    #不修改原始数据集,创建一个新的列表对象进行操作
    retDataSet=[]
    #提取数据集的每一行的特征向量
    for featVec in dataSet:
        #针对axis特征不同的取值,将数据集划分为不同的分支
        #如果该特征的取值为value
        if featVec[axis]==value:
            #将特征向量的0~axis-1列存入列表reducedFeatVec
            reducedFeatVec=featVec[:axis]
            #将特征向量的axis+1~最后一列存入列表reducedFeatVec
            #extend()是将另外一个列表中的元素(以列表中元素为对象)一一添加到当前列表中,构成一个列表
            #比如a=[1,2,3],b=[4,5,6],则a.extend(b)=[1,2,3,4,5,6]
            reducedFeatVec.extend(featVec[axis+1:])
            #简言之,就是将原始数据集去掉当前划分数据的特征列
            #append()是将另外一个列表(以列表为对象)添加到当前列表中
            ##比如a=[1,2,3],b=[4,5,6],则a.extend(b)=[1,2,3,[4,5,6]]
            retDataSet.append(reducedFeatVec)
    return retDataSet

#如何选择最好的划分数据集的特征
#使用某一特征划分数据集,信息增益最大,则选择该特征作为最优特征
def chooseBestFeatureToSplit(dataSet):
    #获取数据集特征的数目(不包含最后一列的类标签)
    numFeatures=len(dataSet[0])-1
    #计算未进行划分的信息熵
    baseEntropy=calEnt(dataSet)
    #最优信息增益    最优特征
    bestInfoGain=0.0;bestFeature=-1
    #利用每一个特征分别对数据集进行划分,计算信息增益
    for i in range(numFeatures):
        #得到特征i的特征值列表
        featList=[example[i] for example in dataSet]
        #利用set集合的性质--元素的唯一性,得到特征i的取值
        uniqueVals=set(featList)
        #信息增益0.0
        newEntropy=0.0
        #对特征的每一个取值,分别构建相应的分支
        for value in uniqueVals:
            #根据特征i的取值将数据集进行划分为不同的子集
            #利用splitDataSet()获取特征取值Value分支包含的数据集
            subDataSet=splitDataSet(dataSet,i,value)
            #计算特征取值value对应子集占数据集的比例
            prob=len(subDataSet)/float(len(dataSet))
            #计算占比*当前子集的信息熵,并进行累加得到总的信息熵
            newEntropy+=prob*calEnt(subDataSet)
        #计算按此特征划分数据集的信息增益
        #公式特征A,数据集D
        #则H(D,A)=H(D)-H(D/A)
        infoGain=baseEntropy-newEntropy
        #比较此增益与当前保存的最大的信息增益
        if (infoGain>bestInfoGain):
            #保存信息增益的最大值
            bestInfoGain=infoGain
            #相应地保存得到此最大增益的特征i
            bestFeature=i
        #返回最优特征
    return bestFeature

#当遍历完所有的特征属性后,类标签仍然不唯一(分支下仍有不同分类的实例)
#采用多数表决的方法完成分类
def majorityCnt(classList):
    #创建一个类标签的字典
    classCount={}
    #遍历类标签列表中每一个元素
    for vote in classList:
        #如果元素不在字典中
        if vote not in classCount.keys():
            #在字典中添加新的键值对
            classCount[vote]=0
        #否则,当前键对于的值加1
        classCount[vote]+=1
    #对字典中的键对应的值所在的列,按照又大到小进行排序
    #@classCount.items 列表对象
    #@key=operator.itemgetter(1) 获取列表对象的第一个域的值
    #@reverse=true 降序排序,默认是升序排序
    sortedClassCount=sorted(classCount.items,\
    key=operator.itemgetter(1),reverse=True)
    #返回出现次数最多的类标签
    return sortedClassCount[0][0]


# 创建树
def createTree(dataSet, labels):
    # 获取数据集中的最后一列的类标签,存入classList列表
    classList = [example[-1] for example in dataSet]
    # 通过count()函数获取类标签列表中第一个类标签的数目
    # 判断数目是否等于列表长度,相同表面所有类标签相同,属于同一类
    if classList.count(classList[0]) == len(classList):
        return classList[0]
    # 遍历完所有的特征属性,此时数据集的列为1,即只有类标签列
    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:
        # 采用递归的方法利用该特征对数据集进行分类
        # @bestFeatLabel 分类特征的特征标签值
        # @dataSet 要分类的数据集
        # @bestFeat 分类特征的标称值
        # @value 标称型特征的取值
        # @subLabels 去除分类特征后的子特征标签列表

        #复制类标签,并将新变量存储在sublables中
        subLabels = labels[:]
        myTree[bestFeatLabel][value] = createTree(splitDataSet \
                                                      (dataSet, bestFeat, value), subLabels)
    return myTree

if __name__ == "__main__":
    myDat, lables = creatDataSet()
    myTree = createTree(myDat, lables)
    print(myTree)

程序结果如下:





  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值