机器学习—决策树(ID3)

时间:2023-02-12 23:17:09

一、相关知识

1 决策树算法

     在构造决策树时,第一个需要解决的问题就是,如何确定出哪个特征在划分数据分类是起决定性作用,或者说使用哪个特征分类能实现最好的分类效果。这样,为了找到决定性的特征,划分川最好的结果,我们就需要评估每个特征。当找到最优特征后,依此特征,数据集就被划分为几个数据子集,这些数据自己会分布在该决策点的所有分支中。此时,如果某个分支下的数据属于同一类型,则该分支下的数据分类已经完成,无需进行下一步的数据集分类;如果分支下的数据子集内数据不属于同一类型,那么就要重复划分该数据集的过程,按照划分原始数据集相同的原则,确定出该数据子集中的最优特征,继续对数据子集进行分类,直到所有的特征已经遍历完成,或者所有叶结点分支下的数据具有相同的分类。
     创建分支的伪代码函数createBranch()如下:

     检测数据集中的每一个子项是否属于同一分类: 

if so return 类标签;
else
      寻找划分数据集的最好特征
      划分数据集
      创建分支结点
            for 每个分支结点
                 调用函数createBranch并增加返回结点到分支结点中//递归调用createBranch()
      return 分支结点

    了解了如何划分数据集后,我们可以总结出决策树的一般流程:
    (1)收集数据:可以使用任何方法
    (2)准备数据:构造树算法只适用于标称型数据,因此数值型数据必须离散化
    (3)分析数据:可以使用任何方法,构造树完成之后,应该检查图形是否符合预期
    (4)训练数据:上述的构造树过程构造决策树的数据结构
    (5)测试算法:使用经验树计算错误率
    (6)使用算法:在实际中更好地理解数据内在含义

2 最好特征选取的规则:信息增益

     划分数据集的大原则是:将无序的数据变得更加有序。在划分数据集前后信息发生的变化称为信息增益,如果我们知道如何计算信息增益,就可以计算每个特征值划分数据集获得的信息增益,而获取信息增益最高的特征就是最好的特征。
     接下来,我们讲学习如何计算信息增益,而提到信息增益我们又不得不提到一个概念"香农熵",或者简称熵。熵定义为信息的期望值。
     如果待分类的事物可能会出现多个结果x,则第i个结果xi发生的概率为p(xi),那么我们可以由此计算出xi的信息熵为l(xi)=p(xi)log(1/p(xi))=-p(xi)log(p(xi))
那么,对于所有可能出现的结果,事物所包含的信息希望值(信息熵)就为:H=-Σp(xi)log(p(xi)),i属于所有可能的结果
     这样,假设利用数据集中某一特征A对数据集D(D的分类类别有n种)进行分类,而特征A取值有k种,那么此时,利用特征A对数据集进行分类的信息增益为:
     信息增益H(D,A)=原始数据集的信息熵H(D)-特征A对数据集进行划分后信息熵H(D/A)
其中H(D/A)=∑|Aj|/|D|*H(Aj),j属于A的k种取值,|Aj|和|D|分别表示,特征A第j种取值的样本数占所有取值样本总数的比例,以及数据集的样本总数

二、构造决策树

      创建名为trees.py的文件将代码全部写到该文件中。

1 计算给定数据集的香农熵

# 计算给定数据集的熵
def clacShannonEnt(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
    shannonEnt = 0.0
    # 对于数据集中所有的分类类别
    for key in labelCounts:
        # 计算各个类别出现的频率
        prob = float(labelCounts[key])/numEntries
        # 计算各个类别信息期望值
        shannonEnt -= prob * log(prob,2)
    return shannonEnt

2 用createDataSet函数创建自己需要的数据集

# 创建自己定义的数据集
def createDataSet():
    dataSet = [[1,1,'yes'],[1,1,'yes'],[1,0,'no'],[0,1,'no'],[0,0,'no']]
    labels = ['no surfacing','flippers']
    return dataSet,labels

        在Python命令提示符下输入下列命令,计算该数据集的香农熵(需要说明的是,熵越高,那么混合的数据就越多,如果我们在数据集中添加更多的分类,会导致熵结果增大):

>>> import trees
>>> myData,labels = trees.createDataSet()
>>> myData
[[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 0, 'no']]
>>> labels
['no surfacing', 'flippers']
>>> trees.clacShannonEnt(myData)
0.9709505944546686

3 划分数据集

      分类算法除了要测量信息熵,还需要划分数据集,度量划分数据集的熵,以便于判断当前是否正确的划分了数据集,对每个特征划分的数据集的结果计算一次信息熵,然后判断按照哪个特征划分数据集是做好的方式:

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

需要说明的是:
     (1)在划分数据集函数中,传递的参数dataSet列表的引用,在函数内部对该列表对象进行修改,会导致列表内容发生改变,于是,为了消除该影响,我们应该在函数中创建一个新的列表对象,将对列表对象操作后的数据集存入新的列表对象中
     (2)需要区分一下append()函数和extend()函数,这两种方法的功能类似,都是在列表末尾添加新元素,但是在处理多个列表时,处理结果有所不同:
        比如:a=[1,2,3],b=[4,5,6]
        那么a.append(b)的结果为:[1,2,3,[4,5,6]],即使用append()函数会在列表末尾添加人新的列表对象b

        而a.extend(b)的结果为:[1,2,3,4,5,6],即使用extend()函数

      可以在Python命令提示符下输入下列命令,划分数据集:

>>> import importlib
>>> importlib.reload(trees)
<module 'trees' from 'E:\\pyCharm\\workspace\\test1\\learning-trees\\trees.py'>
>>> myData,labels = trees.createDataSet()
>>> myData
[[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 0, 'no']]
>>> trees.splitDataSet(myData,0,1)
[[1, 'yes'], [1, 'yes'], [0, 'no']]
>>> trees.splitDataSet(myData,0,0)
[[1, 'no'], [0, 'no']]

4 选择最好的数据集划分方式

        遍历整个数据集,循环计算香农熵和splitDataSet( )函数,找到最好的特征划分方式:

#如何选择最好的划分数据集的特征#使用某一特征划分数据集,信息增益最大,则选择该特征作为最优特征
def chooseBestFeatureToSplit(dataSet):
    # 获取数据集特征的数目(不包含最后一列的类标签)
    numFeatuers = len(dataSet[0])-1
    # 计算未进行划分的信息熵
    baseEntropy = clacShannonEnt(dataSet)
    # 定义最优信息增益    最优特征
    bestInfoGain = 0.0
    bestFeature = -1
    for i in range(numFeatuers):
        # 得到特征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 * clacShannonEnt(subDataSet)
        # 计算按此特征划分数据集的信息增益    # 公式特征A,数据集D    # 则H(D,A)=H(D)-H(D/A)
        infoGain = baseEntropy - newEntropy
        # 比较此增益与当前保存的最大的信息增益
        if(infoGain > bestInfoGain):
            bestInfoGain = infoGain
            bestFeature = i
    return bestFeature

    测试上面代码的事迹输出结果,在Python命令提示符下输入以下命令,得到最好划分数据集特征的索引:

>>> import importlib
>>> importlib.reload(trees)
<module 'trees' from 'E:\\pyCharm\\workspace\\test1\\learning-trees\\trees.py'>
>>> myData,labels = trees.createDataSet()
>>> trees.chooseBestFeatureToSplit(myData)
0
>>> myData
[[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 0, 'no']]

    代码运行告诉我们,第0个特征是最好的用于划分数据的特征。

5 构建决策树

      在通过以上的各个模块学习之后,我们接下来就要真正构建决策树,构建决策树的工作原理为:首先得到原始数据集,然后基于最好的属性划分数据集,由于特征值可能多于两个,因此可能存在大于两个分支的数据集划分。第一次划分之后,数据将向下传递到树分支的下一个结点,在该结点上,我们可以再次划分数据。因此,我们可以采用递归的方法处理数据集,完成决策树构造。
      递归的条件是:程序遍历完所有划分数据集的属性,或者每个分之下的所有实例都具有相同的分类。如果所有的实例具有相同的分类,则得到一个叶子结点或者终止块。
      当然,我们可能会遇到,当遍历完所有的特征属性,但是某个或多个分支下实例类标签仍然不唯一,此时,我们需要确定出如何定义该叶子结点,在这种情况下,通过会采取多数表决的原则选取分支下实例中类标签种类最多的分类作为该叶子结点的分类

      这样,我们就需要先定义一个多数表决函数majorityCnt(),注意需要在 trees.py 顶部添加 import operator

#当遍历完所有的特征属性后,类标签仍然不唯一(分支下仍有不同分类的实例)#采用多数表决的方法完成分类
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]
    # 结束条件一:该分支下所有记录的类别相同,则为叶子节点,停止分类
    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])
    # 获取数据集中最优特征所在列 #采用set集合性质,获取特征的所有的唯一取值
    featValues = [example[bestFeat] for example in dataSet]
    uniqueVals = set(featValues)
    # 遍历每一个特征取值
    # 采用递归的方法利用该特征对数据集进行分类    # @bestFeatLabel 分类特征的特征标签值    # @dataSet 要分类的数据集
    #  @bestFeat 分类特征的标称值    # @value 标称型特征的取值    # @subLabels 去除分类特征后的子特征标签列表
    for value in uniqueVals:
        subLabels = labels[:]
        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet,bestFeat,value),subLabels)
    return myTre

    可以在Python命令提示符窗口测试代码运行效果:

>>> import importlib
>>> importlib.reload(trees)
<module 'trees' from 'E:\\pyCharm\\workspace\\test1\\learning-trees\\trees.py'>
>>> myData,labels = trees.createDataSet()
>>> myTree = trees.creataTree(myData,labels)
>>> myTree
{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}

5 测试算法:使用决策树分类

      利用构建好的决策树,输入符合要求的测试数据,比较测试数据与决策树上的数值,递归执行该过程直到叶子结点,最后将测试数据定义为叶子结点所有的分类,输出分类结果

# 测试算法
# 完成决策树的构造后,采用决策树实现具体应用
# @intputTree 构建好的决策树#@featLabels 特征标签列表#@testVec 测试实例
def classify(inputeTree,featLabels,testVec):
    # 找到树的第一个分类特征,或者说根节点'no surfacing'
    # 注意python2.x和3.x区别,2.x可写成firstStr=inputTree.keys()[0]  # 而不支持3.x 所以改成如下所示
    firstSides = list(inputeTree.keys())
    firstStr = firstSides[0]
    # 从树中得到该分类特征的分支,有0和1
    secondDict = inputeTree[firstStr]
    # 根据分类特征的索引找到对应的标称型数据值
    # 'no surfacing'对应的索引为0
    featIndex = featLabels.index(firstStr)
    # 遍历分类特征所有的取值
    for key in secondDict.keys():
        # 测试实例的第0个特征取值等于第key个子节点
        if testVec[featIndex] == key:
            # type()函数判断该子节点是否为字典类型#子节点为字典类型,则从该分支树开始继续遍历分类#如果是叶子节点,则返回节点取值
            if type(secondDict[key]).__name__ == 'dict':
                classLabel = classify(secondDict[key],featLabels,testVec)
            else:
                classLabel = secondDict[key]
    return classLabel

    打开Python命令提示符,输入以下命令进行测试:

>>> import importlib
>>> importlib.reload(trees)
<module 'trees' from 'E:\\pyCharm\\workspace\\test1\\learning-trees\\trees.py'>
>>> myData,labels = trees.createDataSet()
>>> myTree = trees.creataTree(myData,labels)
>>> trees.classify(myTree,labels,[1,0])
'no'
>>> trees.classify(myTree,labels,[1,1])
'yes'

6 决策树的存储

      我们已经创建好了决策树,但是每次使用分类器时都需要重新构造决策树,这样会十分消耗时间,所以,我们可以将构建好的决策树保存在磁盘中,这样当我们需要的时候,再从磁盘中读取出来使用即可。

      如何进行对象的序列化操作,python的pickle模块足以胜任该任务,任何对象都可以通过pickle模块执行序列化操作,字典也不例外,使用pickle模块存储和读取决策树文件的代码如下:

# 决策树的存储
def storeTree(inputeTree,filename):
    import pickle
    fw = open(filename , 'wb+')
    pickle.dump(inputeTree,fw)
    fw.close()

def grabTree(filename):
    import pickle
    fr = open(filename,'rb')
    return pickle.load(fr)

      在Python命令提示符下输入命令验证效果:

>>> import importlib
>>> importlib.reload(trees)
<module 'trees' from 'E:\\pyCharm\\workspace\\test1\\learning-trees\\trees.py'>
>>> myData,labels = trees.createDataSet()
>>> myTree = trees.creataTree(myData,labels)
>>> myTree
{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}
>>> trees.storeTree(myTree,'trees1.txt')
>>> trees.grabTree('trees1.txt')
{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}
>>>

三、使用matplotlib注解绘制决策树

      字典表示树的形式非常不易于理解,而且直接绘制图形也比较困难,这次将使用matplotlib创建树形图,matplotlib提供了一个非常有用的注解工作annotations,他可以在数据图形上添加文本注解,Matplotlib实际上是一套面向对象的绘图库,它所绘制的图表中的每个绘图元素,例如线条Line2D、文字Text、刻度等在内存中都有一个对象与之对应。

        创建treePlotter.py文件,然后输入下列代码,定义文本框和箭头格式,并且绘制带箭头的注解:

import matplotlib.pyplot as plt
# 定义文本框和箭头格式
# 创建字典。 boxstyle=”sawtooth” 表示 注解框的边缘是波浪线,fc=”0.8” 是颜色深度
decisionNode = dict(boxstyle = "sawtooth", fc = "0.8")
leafNode = dict(boxstyle = "round4", fc = "0.8")
arrow_args = dict(arrowstyle = "<-")

#  centerPt节点中心坐标  parentPt 起点坐标
def plotNode(nodeTxt, centerPt, parentPt, nodeType):
    createPlot.ax1.annotate(nodeTxt, xy=parentPt, xycoords='axes fraction', xytext=centerPt, textcoords='axes fraction',va="center", ha="center", bbox=nodeType, arrowprops=arrow_args)

        我们要知道: 有多少个叶节点,以便确定x轴的长度 ;树有多少层,以便确定y轴的高度。

# 获取叶节点数目
def getNumLeafs(myTree):
    numLeafs = 0
    # 注(a) 获得第一个key值(根节点) 'no surfacing'
    firstSides = list(myTree.keys())
    firstStr = firstSides[0]
    # 获得value值 {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}
    secondDict = myTree[firstStr]
    #  键值:0 和 1
    for key in secondDict.keys():
        if type(secondDict[key]).__name__ == 'dict':
            numLeafs += getNumLeafs(secondDict[key])
        else:
            numLeafs += 1
    return numLeafs

# 获取树的层数
def getTreeDepth(myTree):
    maxDepth = 0
    thisDepth = 0
    firstSides = list(myTree.keys())
    firstStr = firstSides[0]
    secondDict = myTree[firstStr]
    for key in secondDict.keys():
        if type(secondDict[key]).__name__ == 'dict':
            thisDepth = 1 + getTreeDepth(secondDict[key])
        else:
            thisDepth = 1
        if thisDepth > maxDepth:
            maxDepth = thisDepth
    return maxDepth

        预先存储树的信息:

# 预先存储树的信息
def retrieveTree(i):
    listOfTrees = [{'no surfacing':{0:'no',1:{'flippers':{0:'no',1:'yes'}}}},
                   {'no surfacing': {0: 'no', 1: {'flippers': {0: {'head':{0:'no',1:'yes'}}, 1: 'no'}}}}]
    return listOfTrees[i]

        保存文件,并且在Python命令提示符下输入以下命令,查看输的叶子结点数目和层数:

>>> import importlib
>>> importlib.reload(treePlotter)
<module 'treePlotter' from 'E:\\pyCharm\\workspace\\test1\\learning-trees\\treePlotter.py'>
>>> treePlotter.retrieveTree(1)
{'no surfacing': {0: 'no', 1: {'flippers': {0: {'head': {0: 'no', 1: 'yes'}}, 1: 'no'}}}}
>>> myTree = treePlotter.retrieveTree(0)
>>> treePlotter.getNumLeafs(myTree)
3
>>> treePlotter.getTreeDepth(myTree)
2

        添加绘制图的函数

#  在两个节点之间的线上写上字
def plotMidText(cntrPt,parentPt,txtString):
    xMid = (parentPt[0] - cntrPt[0]) / 2.0 + cntrPt[0]
    yMid = (parentPt[1] - cntrPt[1]) / 2.0 + cntrPt[1]
    createPlot.ax1.text(xMid,yMid,txtString)

# 画树
def plotTree(myTree,parentPt,nodeTxt):
    numLeafs = getNumLeafs(myTree)
    depth = getTreeDepth(myTree)
    firstSides = list(myTree.keys())
    firstStr = firstSides[0]
    cntrPt = (plotTree.xOff + (1.0 + float(numLeafs)) / 2.0 / plotTree.totalW,plotTree.yOff)
    plotMidText(cntrPt,parentPt,nodeTxt)
    plotNode(firstStr,cntrPt,parentPt,decisionNode)
    secondDict = myTree[firstStr]
    plotTree.yOff = plotTree.yOff - 1.0 / plotTree.totalD
    for key in secondDict.keys():
        if type(secondDict[key]).__name__ == 'dict':
            plotTree(secondDict[key],cntrPt,str(key))
        else:
            plotTree.xOff = plotTree.xOff + 1.0 / plotTree.totalW
            plotNode(secondDict[key], (plotTree.xOff, plotTree.yOff), cntrPt, leafNode)
            plotMidText((plotTree.xOff, plotTree.yOff), cntrPt, str(key))
    plotTree.yOff = plotTree.yOff + 1.0 / plotTree.totalD

def createPlot(inTree):
    # 创建一个新图形
    fig = plt.figure(1,facecolor='white')
    # 清空绘图区
    fig.clf()
    axprops = dict(xticks=[], yticks=[])  # 创建字典 存储
    createPlot.ax1 = plt.subplot(111, frameon=False, **axprops)
    plotTree.totalW = float(getNumLeafs(inTree))
    plotTree.totalD = float(getTreeDepth(inTree))  # 创建两个全局变量存储树的宽度和深度
    plotTree.xOff = -0.5 / plotTree.totalW  # 追踪已经绘制的节点位置 初始值为 将总宽度平分 在取第一个的一半
    plotTree.yOff = 1.0
    plotTree(inTree, (0.5, 1.0), '')  # 调用函数,并指出根节点源坐标
    plt.show()

        现在可以验证一下实际输出效果,在Python命令提示符下输入以下命令:

>>> import importlib
>>> importlib.reload(treePlotter)
<module 'treePlotter' from 'E:\\pyCharm\\workspace\\test1\\learning-trees\\treePlotter.py'>
>>> treePlotter.retrieveTree(1)
{'no surfacing': {0: 'no', 1: {'flippers': {0: {'head': {0: 'no', 1: 'yes'}}, 1: 'no'}}}}
>>> myTree = treePlotter.retrieveTree(1)
>>> treePlotter.createPlot(myTree)

       得到结果图为:

机器学习—决策树(ID3)

        到目前为止已经知道了如何构决策树以及绘制树形图的方法。

四、使用决策树预测隐形眼镜类型

       隐形眼镜数据集是非常著名的数据集,他包含很多患者眼部状况观察条件以及医生推荐的隐形眼镜类型,隐形眼镜类型包括硬材质,软材质以及不适合佩戴隐形眼镜。数据来源于UCI数据库,数据集如下,(保存在11.txt文件中):

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
young	hyper	yes	normal	hard
pre	myope	no	reduced	no lenses
pre	myope	no	normal	soft
pre	myope	yes	reduced	no lenses
pre	myope	yes	normal	hard
pre	hyper	no	reduced	no lenses
pre	hyper	no	normal	soft
pre	hyper	yes	reduced	no lenses
pre	hyper	yes	normal	no lenses
presbyopic	myope	no	reduced	no lenses
presbyopic	myope	no	normal	no lenses
presbyopic	myope	yes	reduced	no lenses
presbyopic	myope	yes	normal	hard
presbyopic	hyper	no	reduced	no lenses
presbyopic	hyper	no	normal	soft
presbyopic	hyper	yes	reduced	no lenses
presbyopic	hyper	yes	normal	no lenses

        在trees.py文件中添加如下代码

# 使用决策树预测隐形眼镜类型
def predictLensesType(filename):
    #打开文本数据
    fr=open(filename)
    #将文本数据的每一个数据行按照tab键分割,并依次存入lenses
    lenses=[inst.strip().split('\t') for inst in fr.readlines()]
    #创建并存入特征标签列表
    lensesLabels=['age', 'prescript', 'astigmatic', 'tear_rate']
    #根据继续文件得到的数据集和特征标签列表创建决策树
    lensesTree=createTree(lenses,lensesLabels)

        在Python命令提示符下输入下列命令:

>>> Tree = trees.predictLensesType('11.txt')
>>> Tree
{'tear_rate': {'normal': {'astigmatic': {'yes': {'prescript': {'hyper': {'age': {'young': 'hard', 'presbyopic': 'no lenses', 'pre': 'no lenses'}}, 'myope': 'hard'}}, 'no': {'age': {'y
oung': 'soft', 'presbyopic': {'prescript': {'hyper': 'soft', 'myope': 'no lenses'}}, 'pre': 'soft'}}}}, 'reduced': 'no lenses'}}
>>> treePlotter.createPlot(Tree)

机器学习—决策树(ID3)

记住!!!!!

数据集一定要在网上下载,不能直接复制,直接复制过去数据集中的格式会发生莫名其妙的变化,导致无法生成决策树

>>> f = open('11.txt')
>>> lenses=[inst.strip().split('\t') for inst in f.readlines()]
>>> lenses
[['young   myope   no  reduced nolenses'], ['young   myope   no  normal  soft'], ['young   myope   yes reduced nolenses'], ['young   myope   yes normal  hard'], ['young   hyper   no
reduced nolenses'], ['young   hyper   no  normal  soft'], ['young   hyper   yes reduced nolenses'], ['young   hyper   yes normal  hard'], ['pre myope   no  reduced nolenses'], ['pre m
yope   no  normal  soft'], ['pre myope   yes reduced nolenses'], ['pre myope   yes normal  hard'], ['pre hyper   no  reduced nolenses'], ['pre hyper   no  normal  soft'], ['pre hyper
  yes reduced nolenses'], ['pre hyper   yes normal  nolenses'], ['presbyopic  myope   no  reduced nolenses'], ['presbyopic  myope   no  normal  nolenses'], ['presbyopic  myope   yes r
educed nolenses'], ['presbyopic  myope   yes normal  hard'], ['presbyopic  hyper   no  reduced nolenses'], ['presbyopic  hyper   no  normal  soft'], ['presbyopic  hyper   yes reduced
nolenses'], ['presbyopic  hyper   yes normal  nolenses']]
>>> lensesLabels=['age', 'prescript', 'astigmatic', 'tear_rate']
>>> lensesTree=trees.createTree(lenses,lensesLabels)
>>> lensesTree
'young   myope   no  reduced nolenses'

本人亲自经历,一开始还以为代码的问题,实验了好久才发现是数据集的问题,如果直接复制,每次输出树都是数据集的第一行数据,困扰我很久,后来在师兄的帮助下,知道是数据集的问题,从GitHub下载原数据集就不会出现以上问题,切记切记!!!