机器学习算法系列之决策树

本系列机器学习的文章打算从机器学习算法的一些理论知识、python实现该算法和调一些该算法的相应包来实现。

目录

一、决策树原理

1、决策树的模型与学习

二、决策树基于python实现

三、基于sklearn实现


一、决策树原理

1、决策树的模型与学习

决策树简介:决策树是一种典型的分类方法。首先对数据进行处理,利用归纳算法生成可读的规则和决策树,然后使用决策树对新数据进行分析。本质上决策树是通过一系列的规则对数据进行分类的过程。

决策树的优点:1、推理过程容易理解,决策过程可以表示为if-then形式;2、推理过程完全依赖于属性变量的取值特点;3、可自动忽略目标变量没有贡献的属性变量,也为判断属性变量的重要性,减少变量的数目提供参考。

1、决策树算法

于决策树算法相关的算法包括:CLS,ID3,C4.5,CART。

决策树的基本组成部分:决策结点、分支和叶子。

决策树中最上面的结点称为根节点,是整个决策树的开始。每个分支是一个新的决策结点,或者是树的叶子。每个决策结点代表一个问题或者决策,通常对应待分类的属性。每个叶子结点代表一种可能分类的结果。

一颗决策是否购买电脑的决策树

在沿着决策树从上到下遍历的过程中,在每个结点都有一个测试。对每个结点上问题的不同测试输出导致 不同的分支,最后会到达一个叶子结点。这一过程就是利用决策树进行分类的过程,利用若干个变量来判断属性的类别。

决策树学习的本质上是从训练数据集中归纳出一组分类规则,于训练数据集不矛盾的决策树, 而能对训练数据集能够正确分类的决策树可能有多个,也可能是一个都没有,我们需要的是一个于训练数据矛盾较小的决策树,同时具有很好的泛化能力。我们最终选择的条件概率模型不仅对训练数据能够有很好的预测,而去对未知数据也能有很好的预测。

2、信息增益

熵(entropy): 信息量大小的度量, 即表示随机变量不确定性的度量。

熵的通俗解释:事件ai的信息量I( ai )可如下度量:其中p(ai)表示事件ai发生的概率。

假设有n个互不相容的事件a1,a2,a3,….,an,它们中有且仅有一个发生, 则其平均的信息量(熵)可如下度量:

熵的理论解释:设X是一个取有限个值的离散随机变量, 其概率分布为:则随机变量X的熵定义为:

熵越大,随机变量的不确定性越大。举例:当为0,1分布时,如果pi=1,那么H(x)=0,可以明显看出熵值为0,因为随机变量x已经确定了为1。

条件熵H(Y|X): 表示在己知随机变量X的条件下随机变量Y的不确定性, 定义为X给定条件下Y的条件概率分布的熵对X的数学期望:

当熵和条件熵中的概率由数据估计(特别是极大似然估计)得到时, 所对应的熵与条件熵分别称为经验熵(empirical entropy)和经验条件熵(empirical conditional entropy )

 

信息增益定义:特征A对训练数据集D的信息增益,g(D,A), 定义为集合D的经验熵H(D)与特征A给定条件下D的经验条件熵H(D|A)之差, 即:g(D,A)=H(D)-H(D|A)

表示得知特征X的信息而使的类Y的信息的不确定性减少的程度。—般地, 熵H(Y)与条件熵H(Y|X)之差称为互信息(mutual information)
决策树学习中的信息增益等价于训练数据集中类与特征的互信息。

信息增益算法流程:

输入: 训练数据集D和特征A;
输出: 特征A对训练数据集D的信息增益g(D,A)
1、 计算数据集D的经验熵H(D)
2、 计算特征A对数据集D的经验条件熵H(D|A)
3、 计算信息增益

这里为什么要引入信息增益的概念呢? 因为后面我们在用决策树算法构建一颗决策树时,需要利用信息增益来确定我们分裂结点优先选用哪个特征来进行分类,因为分类特征选取的顺序不同也会导致决策树的划分不同。

3、决策树的生成

ID3算法:ID3算法是一种经典的决策树学习算法, 由Quinlan于1979年提出。ID3算法主要针对属性选择问题。 是决策树学习方法中最具影响和最为典型的算法。该方法使用信息增益度选择测试属性。当获取信息时, 将不确定的内容转为确定的内容, 因此信息伴着不确定性。从直觉上讲, 小概率事件比大概率事件包含的信息量大。 如果某件事情是“百年一见” 则肯定比“习以为常” 的事件包含的信息量大。
在决策树分类中, 假设S是训练样本集合, |S|是训练样本数, 样本划分为n个不同的类C1,C2,….Cn, 这些类的大小分别标记为|C1|, |C2|, …..,|Cn|。 

 

ID3算法计算流程:

1 决定分类属性;
2 对目前的数据表, 建立一个节点N
3 如果数据库中的数据都属于同一个类, N就是树叶, 在树叶上标出所属的类
4 如果数据表中没有其他属性可以考虑, 则N也是树叶, 按照少数服从多数的原则在树叶上标出所属类别
5 否则, 根据平均信息期望值E或GAIN值选出一个最佳属性作为节点N的测试属性
6 节点属性选定后, 对于该属性中的每个值:
从N生成一个分支, 并将数据表中与该分支有关的数据收集形
成分支节点的数据表, 在表中删除节点属性那一栏如果分支数
据表非空, 则运用以上算法从该节点建立子树。

假设我们得到一批数据:

我们用这些数据来讲解构建一个决策树的流程:

第1步:计算决策属性的熵决策属性“买计算机”/结果:买/不买)

统计可以得到 |C1|(买)=641,|C2|(不买)=383    |D|=|C1|+|C2|=1024   可以计算出某类占总数的概率:p1=641/1021=0.6260  p2=0.3740。 这时候可以计带入经验熵计算公式:算出数据集的经验熵:

 

第二步:计算条件属性的熵

从上表中可以看出条件属性一共有四个:年龄、收入、学生、信誉。分别计算不同属性的信息增益。

条件熵计算公式

这里只举一个年龄属性的计算方法(其他属性计算方法一样。)

年龄共分三组:青年、中年、老年。

青年买与不买比例为128/256  |D11|(青年人买)=128,|D12|(青年不买)=256  |D1|=384, p1=128/384  p2=256/384

带入公式可以计算出:H(D1)=0.9138。

中年买与不买比例为256/0  |D21|(中年人买)=256,|D22|(中年不买)=0  |D2|=256, p1=256/256  p2=0/256

同理带入熵计算公式得出:H(D2)=0

老年买与不买比例为125/127  |D31|(老年人买)=125,|D32|(老年不买)=127  |D3|=252, p1=125/252  p2=127/252

同理带入熵计算公式得出:H(D3)= 0.9157

各类所占比例:青年:384/1024=0.375;中年:256/1024=0.25;老年:384/1024=0.375

计算年龄的平均信息期望:E(年龄)=0.375*0.9138+0.25*0+0.375*0.9157=0.6877;  G(年龄信息增益)=0.9537-0.6877=0.2660

同样的方法计算出其他属性的增益信息为:

收入信息增益=0.9537-0.9361=0.0176 
学生信息增益=0.9537-0.7811=0.1726 
信誉信息增益=0.9537-0.9048=0.0453 

可以看到年龄的信息增益值最大,所以年龄这个分类属性首先就被归类为分裂结点。

在接下来的分裂过程中同样使用这样的办法,以此来确定最终的决策树。

总结:ID3算法的基本思想是, 以信息熵为度量, 用于决策树节点的属性选择, 每次优先选取信息量最多的属性, 亦即能使熵值变为最小的属性, 以构造一颗熵值下降最快的决策树, 到叶子节点处的熵值为0。 此时, 每个叶子节点对应的实例集中的实例属于同一类。

4、决策树的剪枝

决策树剪枝通过极小化决策树整体函数和损失函数来实现。

设树T的叶结点个数为|T|,t是树T的叶结点, 该叶结点有Nt个样本点, 其中k类的样本点有Ntk个, k=1,2..K,Ht(T)为叶结点t上的经验熵, α≥0为参数, 损失函数:

最终:

树的剪枝算法:

输入:生成算法产生的整个树T,参数alpha;

输出:修建后的子树T(α)

(1)计算每个结点的经验熵

(2)递归地从树的结点向上回缩,设一组叶结点回缩到其父结点之前与之和的损失函数分
别为:如果: 则进行剪枝

(3)返回(2),直至不能继续为止,得到损失函数最小地子树。

 


二、决策树基于python实现

 

# -*- coding: utf-8 -*-
"""
用字典存储决策树结构:
{'有自己的房子':{0:{'有工作':{0:'no', 1:'yes'}}, 1:'yes'}}
年龄:0代表青年,1代表中年,2代表老年
有工作:0代表否,1代表是
有自己的房子:0代表否,1代表是
信贷情况:0代表一般,1代表好,2代表非常好
类别(是否给贷款):no代表否,yes代表是

pickle包可以将决策树保存下来,方便下次直接调用
"""
from matplotlib.font_manager import FontProperties
import matplotlib.pyplot as plt
from math import log
import operator
import pickle

"""
函数说明:创建测试数据集

Parameters:
    None
    
Returns:
    dataSet - 数据集
    labels - 分类属性

"""
def createDataSet():
    # 数据集
    dataSet = [[0, 0, 0, 0, 'no'],
               [0, 0, 0, 1, 'no'],
               [0, 1, 0, 1, 'yes'],
               [0, 1, 1, 0, 'yes'],
               [0, 0, 0, 0, 'no'],
               [1, 0, 0, 0, 'no'],
               [1, 0, 0, 1, 'no'],
               [1, 1, 1, 1, 'yes'],
               [1, 0, 1, 2, 'yes'],
               [1, 0, 1, 2, 'yes'],
               [2, 0, 1, 2, 'yes'],
               [2, 0, 1, 1, 'yes'],
               [2, 1, 0, 1, 'yes'],
               [2, 1, 0, 2, 'yes'],
               [2, 0, 0, 0, 'no']]
    # 分类属性
    labels = ['年龄', '有工作', '有自己的房子', '信贷情况']
    # 返回数据集和分类属性
    return dataSet, labels


"""
函数说明:计算给定数据集的经验熵(香农熵)
        Ent(D) = -SUM(kp*Log2(kp))

Parameters:
    dataSet - 数据集
    
Returns:
    shannonEnt - 经验熵(香农熵)

"""
def calcShannonEnt(dataSet):
    # 返回数据集的行数
    numEntires = len(dataSet)
    # 保存每个标签(Label)出现次数的“字典”
    labelCounts = {}
    # 对每组特征向量进行统计
    for featVec in dataSet:
        # 提取标签(Label)信息
        currentLabel = featVec[-1]
        # 如果标签(Label)没有放入统计次数的字典,添加进去
        if currentLabel not in labelCounts.keys():
            # 创建一个新的键值对,键为currentLabel值为0
            labelCounts[currentLabel] = 0
        # Label计数
        labelCounts[currentLabel] += 1
    # 经验熵(香农熵)
    shannonEnt = 0.0
    # 计算香农熵
    for key in labelCounts:
        # 选择该标签(Label)的概率
        prob = float(labelCounts[key]) / numEntires
        # 利用公式计算
        shannonEnt -= prob*log(prob, 2)
    # 返回经验熵(香农熵)
    return shannonEnt

"""
函数说明:按照给定特征划分数据集

Parameters:
    dataSet - 待划分的数据集
    axis - 划分数据集的特征
    values - 需要返回的特征的值
    
Returns:
    None
    
"""
def splitDataSet(dataSet, axis, value):
    # 创建返回的数据集列表
    retDataSet = []
    # 遍历数据集的每一行
    for featVec in dataSet:
        if featVec[axis] == value:
            # 去掉axis特征
            reducedFeatVec = featVec[:axis]
            # 将符合条件的添加到返回的数据集
            # extend() 函数用于在列表末尾一次性追加另一个序列中的多个值(用新列表扩展原来的列表)。
            reducedFeatVec.extend(featVec[axis+1:])
            # 列表中嵌套列表
            retDataSet.append(reducedFeatVec) 
    # 返回划分后的数据集
    return retDataSet
    

"""
函数说明:选择最优特征
        Gain(D,g) = Ent(D) - SUM(|Dv|/|D|)*Ent(Dv)

Parameters:
    dataSet - 数据集
    
Returns:
    bestFeature - 信息增益最大的(最优)特征的索引值
"""
def chooseBestFeatureToSplit(dataSet):
    # 特征数量
    numFeatures = len(dataSet[0]) - 1
    # 计算数据集的香农熵
    baseEntropy = calcShannonEnt(dataSet)
    # 信息增益
    bestInfoGain = 0.0
    # 最优特征的索引值
    bestFeature = -1
    # 遍历所有特征
    for i in range(numFeatures):
        # 获取dataSet的第i个所有特征存在featList这个列表中(列表生成式)
        featList = [example[i] for example in dataSet]
        # 创建set集合{},元素不可重复,重复的元素均被删掉
        # 从列表中创建集合是python语言得到列表中唯一元素值得最快方法
        uniqueVals = set(featList)
        # 经验条件熵
        newEntropy = 0.0
        # 计算信息增益
        for value in uniqueVals:
            # subDataSet划分后的子集
            subDataSet = splitDataSet(dataSet, i, value)
            # 计算子集的概率
            prob = len(subDataSet) / float(len(dataSet))
            # 根据公式计算经验条件熵
            newEntropy += prob * calcShannonEnt(subDataSet)
        # 信息增益
        infoGain = baseEntropy - newEntropy
        # 打印每个特征的信息增益
        print("第%d个特征的增益为%.3f" % (i, infoGain))
        # 计算信息增益
        if(infoGain > bestInfoGain):
            # 更新信息增益,找到最大的信息增益
            bestInfoGain = infoGain
            # 记录信息增益最大的特征的索引值
            bestFeature = i
    # 返回信息增益最大的特征的索引值
    return bestFeature


"""
函数说明:统计classList中出现次数最多的元素(类标签)
        服务于递归第两个终止条件

Parameters:
    classList - 类标签列表
    
Returns:
    sortedClassCount[0][0] - 出现次数最多的元素(类标签)
    
"""   
def majorityCnt(classList):
    classCount = {}
    # 统计classList中每个元素出现的次数
    for vote in classList:
        if vote not in classCount.keys():
            classCount[vote] = 0
        classCount[vote] += 1
    # 根据字典的值降序排序
    # operator.itemgetter(1)获取对象的第1列的值
    sortedClassCount = sorted(classCount.items(), key = operator.itemgetter(1), reverse = True)
    # 返回classList中出现次数最多的元素
    return sortedClassCount[0][0]


"""
函数说明:创建决策树(ID3算法)
        递归有两个终止条件:1、所有的类标签完全相同,直接返回类标签
                        2、用完所有标签但是得不到唯一类别的分组,即特征不够用,挑选出现数量最多的类别作为返回

Parameters:
    dataSet - 训练数据集
    labels - 分类属性标签
    featLabels - 存储选择的最优特征标签
    
Returns:
    myTree - 决策树
    
"""
def createTree(dataSet, labels, featLabels):
    # 取分类标签(是否放贷:yes or no)
    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]
    featLabels.append(bestFeatLabel)
    # 根据最优特征的标签生成树
    myTree = {bestFeatLabel:{}}
    # 删除已经使用的特征标签
    del(labels[bestFeat])
    # 得到训练集中所有最优解特征的属性值
    featValues = [example[bestFeat] for example in dataSet]
    # 去掉重复的属性值
    uniqueVals = set(featValues)
    # 遍历特征,创建决策树
    for value in uniqueVals:
        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), labels, featLabels)
    return myTree


"""
函数说明:获取决策树叶子结点的数目

Parameters:
    myTree - 决策树
    
Returns:
    numLeafs - 决策树的叶子结点的数目
    
""" 
def getNumLeafs(myTree):
    # 初始化叶子
    numLeafs = 0
    # python3中myTree.keys()返回的是dict_keys,不是list,所以不能用
    # myTree.keys()[0]的方法获取结点属性,可以使用list(myTree.keys())[0]
    # next() 返回迭代器的下一个项目 next(iterator[, default])
    firstStr = next(iter(myTree))
    # 获取下一组字典
    secondDict = myTree[firstStr]
    for key in secondDict.keys():
        # 测试该结点是否为字典,如果不是字典,代表此节点为叶子结点
        if type(secondDict[key]).__name__ == 'dict':
            numLeafs += getNumLeafs(secondDict[key])
        else:
            numLeafs += 1
    return numLeafs


"""
函数说明:获取决策树的层数

Parameters:
    myTree - 决策树
    
Returns:
    maxDepth - 决策树的层数
    
"""   
def getTreeDepth(myTree):
    # 初始化决策树深度
    maxDepth = 0
    # python3中myTree.keys()返回的是dict_keys,不是list,所以不能用
    # myTree.keys()[0]的方法获取结点属性,可以使用list(myTree.keys())[0]
    # next() 返回迭代器的下一个项目 next(iterator[, default])
    firstStr = next(iter(myTree))
    # 获取下一个字典
    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


"""
函数说明:绘制结点

Parameters:
    nodeTxt - 结点名
    centerPt - 文本位置
    parentPt - 标注的箭头位置
    nodeType - 结点格式
    
Returns:
    None
    
"""  
def plotNode(nodeTxt, centerPt, parentPt, nodeType):
    # 定义箭头格式
    arrow_args = dict(arrowstyle="<-")
    # 设置中文字体
    font = FontProperties(fname=r"C:\Windows\Fonts\simsun.ttc", size=14)
    # 绘制结点createPlot.ax1创建绘图区
    # annotate是关于一个数据点的文本
    # nodeTxt为要显示的文本,centerPt为文本的中心点,箭头所在的点,parentPt为指向文本的点
    createPlot.ax1.annotate(nodeTxt, xy=parentPt, xycoords='axes fraction', 
                            xytext=centerPt, textcoords='axes fraction',
                            va='center', ha='center', bbox=nodeType, 
                            arrowprops=arrow_args, FontProperties=font)


"""
函数说明:标注有向边属性值

Parameters:
    cntrPt、parentPt - 用于计算标注位置
    txtString - 标注内容
    
Returns:
    None
    
""" 
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, va="center", ha="center", rotation=30)


"""
函数说明:绘制决策树

Parameters:
    myTree - 决策树(字典)
    parentPt - 标注的内容
    nodeTxt - 结点名
    
Returns:
    None
    
""" 
def plotTree(myTree, parentPt, nodeTxt):
    # 设置结点格式boxstyle为文本框的类型,sawtooth是锯齿形,fc是边框线粗细
    decisionNode = dict(boxstyle="sawtooth", fc="0.8")
    # 设置叶结点格式
    leafNode = dict(boxstyle="round4", fc="0.8")
    # 获取决策树叶结点数目,决定了树的宽度
    numLeafs = getNumLeafs(myTree)
    # 获取决策树层数
    depth = getTreeDepth(myTree)
    # 下个字典
    firstStr = next(iter(myTree))
    # 中心位置
    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]
    # y偏移
    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
    

"""
函数说明:创建绘图面板

Parameters:
    inTree - 决策树(字典)
    
Returns:
    None
    
""" 
def createPlot(inTree):
    # 创建fig
    fig = plt.figure(1, facecolor="white")
    # 清空fig
    fig.clf()
    axprops = dict(xticks=[], yticks=[])
    # 去掉x、y轴
    createPlot.ax1 = plt.subplot(111, frameon=False, **axprops)
    # 获取决策树叶结点数目
    plotTree.totalW = float(getNumLeafs(inTree))
    # 获取决策树层数
    plotTree.totalD = float(getTreeDepth(inTree))
    # x偏移
    plotTree.xoff = -0.5 / plotTree.totalW
    plotTree.yoff = 1.0
    # 绘制决策树
    plotTree(inTree, (0.5, 1.0), '')
    # 显示绘制结果
    plt.show()
    
   
"""
函数说明:使用决策树分类

Parameters:
    inputTree - 已经生成的决策树
    featLabels - 存储选择的最优特征标签
    testVec - 测试数据列表,顺序对应最优特征标签
    
Returns:
    classLabel - 分类结果
    
""" 
def classify(inputTree, featLabels, testVec):
    # 获取决策树结点
    firstStr = next(iter(inputTree))
    # 下一个字典
    secondDict = inputTree[firstStr]
    featIndex = featLabels.index(firstStr)
    for key in secondDict.keys():
        if testVec[featIndex] == key:
            if type(secondDict[key]).__name__ == 'dict':
                classLabel = classify(secondDict[key], featLabels, testVec)
            else:
                classLabel = secondDict[key]
    return classLabel


"""
函数说明:存储决策树

Parameters:
    inputTree - 已经生成的决策树
    filename - 决策树的存储文件名
    
Returns:
    None
"""   
def storeTree(inputTree, filename):
    with open(filename, 'wb') as fw:
        pickle.dump(inputTree, fw)


"""
函数说明:读取决策树

Parameters:
    filename - 决策树的存储文件名
    
Returns:
    pickle.load(fr) - 决策树字典
""" 
def grabTree(filename):
    fr = open(filename, 'rb')
    return pickle.load(fr)


"""
函数说明:main函数

Parameters:
    None
    
Returns:
    None
"""   
def main():
    dataSet, features = createDataSet()
    featLabels = []
    myTree = createTree(dataSet, features, featLabels)
    # 测试数据
    testVec = [0, 1, 1, 1]
    result = classify(myTree, featLabels, testVec)
    if result == 'yes':
        print('放贷')
    if result == 'no':
        print('不放贷')
    print(myTree)
    print("最终地决策树为:\n")
    createPlot(myTree)
    print("最优特征索引值:" + str(chooseBestFeatureToSplit(dataSet)))

if __name__ == '__main__':
    main()
    

最后构建出地决策树为:


三、基于sklearn实现

数据:数据的Labels依次是age、prescript、astigmatic、tearRate、class
年龄、症状、是否散光、眼泪数量、分类标签

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
 

代码:

# -*- coding: utf-8 -*-
"""
数据的Labels依次是age、prescript、astigmatic、tearRate、class
年龄、症状、是否散光、眼泪数量、分类标签
"""
from sklearn.preprocessing import LabelEncoder, OneHotEncoder
import pandas as pd
import numpy as np
import pydotplus
from sklearn.externals.six import StringIO
from sklearn import tree
from IPython.display import display, Image

if __name__ == '__main__':
    # 加载文件
    with open('lenses.txt') as fr:
        # 处理文件,去掉每行两头的空白符,以\t分隔每个数据
        lenses = [inst.strip().split('\t') for inst in fr.readlines()]
    # 提取每组数据的类别,保存在列表里
    lenses_targt = []
    for each in lenses:
        # 存储Label到lenses_targt中
        lenses_targt.append([each[-1]])
    # 特征标签
    lensesLabels = ['age', 'prescript', 'astigmatic', 'tearRate']
    # 保存lenses数据的临时列表
    lenses_list = []
    # 保存lenses数据的字典,用于生成pandas
    lenses_dict = {}
    # 提取信息,生成字典
    for each_label in lensesLabels:
        for each in lenses:
            # index方法用于从列表中找出某个值第一个匹配项的索引位置
            lenses_list.append(each[lensesLabels.index(each_label)])
        lenses_dict[each_label] = lenses_list
        lenses_list = []
    # 打印字典信息
    # print(lenses_dict)
    # 生成pandas.DataFrame用于对象的创建
    lenses_pd = pd.DataFrame(lenses_dict)
    # 打印数据
    # print(lenses_pd)
    # 创建LabelEncoder对象
    le = LabelEncoder()
    # 为每一列序列化
    for col in lenses_pd.columns:
        # fit_transform()干了两件事:fit找到数据转换规则,并将数据标准化
        # transform()直接把转换规则拿来用,需要先进行fit
        # transform函数是一定可以替换为fit_transform函数的,fit_transform函数不能替换为transform函数
        lenses_pd[col] = le.fit_transform(lenses_pd[col])
    # 打印归一化的结果
    # print(lenses_pd)
    # 创建DecisionTreeClassifier()类
    clf = tree.DecisionTreeClassifier(criterion='entropy', max_depth=4)
    # 使用数据构造决策树
    # fit(X,y):Build a decision tree classifier from the training set(X,y)
    # 所有的sklearn的API必须先fit
    clf = clf.fit(lenses_pd.values.tolist(), lenses_targt)
    dot_data = StringIO()
    # 绘制决策树
    tree.export_graphviz(clf, out_file=dot_data, feature_names=lenses_pd.keys(),
                         class_names=clf.classes_, filled=True, rounded=True, 
                         special_characters=True)
    graph = pydotplus.graph_from_dot_data(dot_data.getvalue())
    #预测
    print(clf.predict([[1,1,1,0]]))      
              

最终地决策树:

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值