ML in Action笔记——CH3 决策树

仅个人代码笔记.

算法介绍

决策树
基本原理是:给定原始数据集,基于最好的特征划分数据集,根节点为该特征,分支为对应的不同的属性值,有多少种属性值,就有多少个分支。不同分支指向的节点是去除分支上属性值后的数据子集。节点上的数据子集可以再次依照相同的方式被划分,所以此处使用递归的思想。
递归结束的条件:程序遍历完所有可用于划分的特征,或者每个分支下所有实例属于相同的分类。

优点:计算复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可以处理不相关特 征数据。
缺点:可能会产生过度匹配问题。
适用数据类型:数值型和标称型。

代码展示

#!usr/bin/python
# -*- coding: utf-8 -*-
from math import log
import operator
import matplotlib.pyplot as plt
import treeplot

'''
学习从一堆原始数据构造决策树:
1.从数据集构造决策树
2.度量算法成功率(信息增益)
3.使用递归建立分类器
4.绘制决策树

决策树的构造:
如何使用信息论划分数据集
编写代码将理论应用于实际数据集
编写代码构建决策树

问题:当前数据集上哪个特征在划分数据集时起决定作用?
解决办法:为了找到决定性的特征,划分出最好的结果,必须评估每个特征

评估方法:
划分数据集的大原则是:将无序的数据变得更加有序。
本章采用ID3算法划分数据集
ID3算法将信息增益做贪心算法来划分算法,在划分数据集的前后信息发生的变化称为信息增益,总是挑选的信息增益最大的特征来划分数据,使得数据更加有序

'''

# 创建数据集
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

# 计算信息增益

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 splitDataSet(dataSet, axis, value):
    #程序清单3-2的代码使用了三个输入参数:待划分的数据集、划分数据集的特征、需要返回的特征的值。
    retDataSet = []
    for featVec in dataSet:
        if featVec[axis] == value:
            '''
            axis=1,value=0时,
            以第1个特征作为判断,若第1个特征值==value,
            则输出当前数据样本的其他特征的值存入reducedFeatVec
            '''
            #当按照某个特征划分数据集时,需要将所有符合要求的元素抽取出来。
            reducedFeatVec = featVec[:axis]
            # extend函数是将featVec的其他特征的值存入列表reducedFeatVec中
            reducedFeatVec.extend(featVec[axis+1:])
            # append函数是将元素reducedFeatVec作为列表插入列表retDataSet中
            retDataSet.append(reducedFeatVec)
    return retDataSet

# 选择最好的数据集划分方式
# 函数实现:选取特征值,划分数据集,计算得到最 好的划分数据集的特征
def chooseBestFeatureToSplit(dataSet):
    numFeatures = len(dataSet[0]) - 1  #计算每个数据样本共有多少个特征
    baseEntropy = calcShannonEnt(dataSet)  #计算原始数据集的熵
    bestInfoGain = 0.0; bestFeature = -1   #初始化最好的信息增益、最好的用于划分数据集的特征下表
    for i in range(numFeatures): #遍历全部特征,计算每个特征的信息增益并选择最好的
        '''
        featList = [example[i] for example in dataSet]
        该语句是将列表featList取一列元素数据的方法
        先提取一行dataSet的数据,然后取该行数据的第“ i ”位元素
        然后遍历每一行,最后获得一整列数据,变为一个列表list
        如:
        dataSet=[[1,1,'yes'],
         [1,1,'yes'],
         [1,0,'no'],
         [0,1,'no'],
         [0,1,'no']]
        featList1=[example[0] for example in dataSet]
        print('featList1 = \n',featList1)
        print(type(featList1))
        >>> featList1 = 
        >>> [1, 1, 1, 0, 0]
        >>> <class 'list'>
        
        featList2=[example[-1] for example in dataSet]
        print('featList2 = \n',featList2)
        >>> featList2 = 
        >>> ['yes', 'yes', 'no', 'no', 'no']
        '''
        featList = [example[i] for example in dataSet]  #创建对应特征的属性值列表
        uniqueVals = set(featList)  #得到当前特征下的不同属性值集合
        newEntropy = 0.0   #初始化熵

        #计算对样本集进行划分所获得的信息增益
        for value in uniqueVals:
            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

# 递归构建决策树
# 分类叶子节点:多数表决的方法
def majorityCnt(classList):
    classCount = {}
    for vote in classList:
        if vote not in classCount.keys():classCount[vote] = 0
        classCount[vote] += 1
    sortedClassCount = sorted(classCount.iteritems(),\
                              key=operator.itemgetter((1),reverse=True))

    return sortedClassCount[0][0]

# 创建树
'''
基本原理是:
给定原始数据集,基于最好的特征划分数据集,根节点为该特征,
分支为对应的不同的属性值,有多少种属性值,就有多少个分支。
不同分支指向的节点是去除分支上属性值后的数据子集。
节点上的数据子集可以再次依照相同的方式被划分,所以此处使用递归的思想。
递归结束的条件:程序遍历完所有可用于划分的特征,或者每个分支下所有实例属于相同的分类。
'''
def createTree(dataSet,labels):
    classList = [example[-1] for example in dataSet]
    #count函数:统计列表classList中classList[0]元素出现的次数
    #print classList
    #>>>['yes', 'yes', 'no', 'no', 'no']
    #print classList[0]
    #>>>yes

    #递归结束条件1:数据集类别属于同一类
    if classList.count(classList[0]) == len(classList):
        return classList[0]
    #递归结束条件2:遍历完所有特征时,返回出现次数最多的特征
    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]
    uniqueVals = set(featValues)   #最好特征下的属性集合
    for value in uniqueVals:
        subLabels = labels[:]
        # 使用划分后的数据子集 和 剩下的特征列表  递归构建字典
        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet,bestFeat,value),subLabels)
    return myTree

# 绘制决策树图()

#测试算法:
# 使用决策树执行分类,即:建好了树之后,拿一条新的数据进行分类
# 使用决策树的分类函数:每次使用分类器时,必须重新构造决策树
def classify(inputTree,featLabels,testVec):
    firstStr = inputTree.keys()[0]  #当前绘制的决策树图中树的根节点的特征名称
    secondDict = inputTree[firstStr]  #根节点的所有子节点
    featIndex = featLabels.index(firstStr)  #找到根节点特征对应的下标
    key = testVec[featIndex]  #找出待测数据的特征值
    valueOfFeat = secondDict[key]  #拿这个特征值在根节点的子节点中查找,判断是不是叶节点

    # 如果不是叶节点,递归到下一层节点; 如果是叶节点:确定待测数据的分类
    if isinstance(valueOfFeat, dict):
        classLabel = classify(valueOfFeat, featLabels, testVec)
    else: classLabel = valueOfFeat
    return classLabel

#决策树的存储:
# 不希望每次拿新数据进行分类前,都构造一遍决策树。所以得想个办法把它存到硬盘上
# 用pickle这个模块,用来将对象持久化
# 就是把建好的决策树(dict类型)存进或者取出文件
def storeTree(inputTree, filename):
    import pickle
    fw = open(filename, 'w')
    pickle.dump(inputTree, fw)
    fw.close()

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

if __name__ == "__main__":
    myDat,labels = createDataset()
    # print myDat
    # print splitDataSet(myDat,0,0)
    # print chooseBestFeatureToSplit(myDat)
    myTree = treeplot.retrieveTree(0)
    print myTree
    print classify(myTree,labels,[1,0])

    storeTree(myTree, 'storage.txt')
    print grabTree('storage.txt')
    
	#使用决策树预测隐形眼镜类型
    fp = open('lenses.txt')
    lensesDataSet = [line.strip().split('\t') for line in fp.readlines()]
    lensesFeatNames = ['age', 'prescript', 'astigmatic', 'tearRate']
    lensesTree = createTree(lensesDataSet, lensesFeatNames)
    print lensesTree
    treeplot.createPlot(lensesTree)

输出结果

D:\My_Program_List(zs)\ML_in_Action\venv\Scripts\python27.exe D:/My_Program_List(zs)/ML_in_Action/tree/tree.py
{'tearRate': {'reduced': 'no lenses', 'normal': {'astigmatic': {'yes': {'prescript': {'hyper': {'age': {'pre': 'no lenses', 'presbyopic': 'no lenses', 'young': 'hard'}}, 'myope': 'hard'}}, 'no': {'age': {'pre': 'soft', 'presbyopic': {'prescript': {'hyper': 'soft', 'myope': 'no lenses'}}, 'young': 'soft'}}}}}}

Process finished with exit code 0

绘制决策树

在这里插入图片描述

©️2020 CSDN 皮肤主题: 终极编程指南 设计师:CSDN官方博客 返回首页