机器学习实战——1.2决策树(2)

声明:参考书目《机器学习实战》作者: Peter Harrington 出版社: 人民邮电出版社 译者: 李锐 / 李鹏 / 曲亚东 / 王斌 

声明:参考书目《统计学习方法》作者: 李航  出版社: 清华大学出版社   ISBN: 9787302275954

声明:参考书目《机器学习》作者: 周志华  出版社: 清华大学出版社  ISBN: 9787302423287

参考博客 Jack-Cui作者个人网站:http://cuijiahua.com/

一递归创建决策树

目前我们已经学习了从数据集构造决策树算法需要的 子功能模块,其工作原理如下:得到原始数据集,然后基于最好的数据特征划分数据集,由于特征值可能多于两个,因此可能存在大于两个分支的数据集划分。第一次划分之后,数据将被传递到树的分支下个节点,在这个节点上,我们可以继续划分数据集。由此可见,我们将采用递归调用的方式来处理数据集。

递归结束的条件是:(1)程序遍历完所有划分数据集的特征属性;(2)每个分支下的所有实例都具有相同的分类。如果所有实例都具有相同的分类,则得到一个叶子结点或者终止节点。任何达到叶子结点的数据必然属于叶子结点的分类。

第一个结束条件使得算法可以终止,我们甚至可以设置算法可以划分的最大组数目。如果数据集已经处理了所有的特征,但是类标签仍不是唯一的,这时我们就需要使用举手表决的方法来少数服从多数了。我们会选择类数目最多的分类。


import operator
"""
classlist:给定的数据字典
function:如果决策树仍然不能正确分类,那么就采取举手表达的措施选择类数最大的类别
"""
def majorityCnt(classlist):
    classCount = {}                             #创建一个字典,用于存放最大的类别
    for vote in classlist:
        if vote not in classCount.keys():
            classCount[vote] = 0
        classCount[vote] += 1
    sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]

该函数使用分类名称的列表,然后创建键值为  classList  中唯一的数据字典,字典对象存储了  classList  中每个类标签出现的频率,最后利用 operator  操作键值排序字典,并返回出现次数最多的分类名称。

然后我们就可以创建决策树了:


"""
dataSet:给定的数据集
lables:给定的标签
return:返回的是构造好的决策树
"""
def createTree(dataSet,lables):
    class_list = [example[-1]  for example in dataSet]         #获取数据集的类别
    if class_list.count(class_list[0]) == len(class_list):      #如果数据集都是一种类别的话,那么就返回这个类别
        return class_list[0]
    if len(dataSet[0]) == 1:                                    #如果遍历完所有数据任有数据不能正确分类,那么就采用举手表决的方式,选择数据类最大的一个类别
        return majorityCnt(class_list)
    bestFeat = chooseBestFeature(dataSet)                      #获取最佳特征的索引值
    bestFeatLabel = lables[bestFeat]                           #根据索引值在标签列表里面获得标签的名称
    myTree = {bestFeatLabel:{}}                                #创建一个字典,这个字典用于存放决策树的数据结构
    del(lables[bestFeat])                                      #获得了我们需要的标签之后,就将标签从标签列表里面删除,防止产生死循环
    featValue = [example[bestFeat] for example in dataSet]     #从数据集获得对应标签的所有数据,即最好的特征数据的值
    uniqueValue = set(featValue)                               #利用集合将重复的特征数据的值删除。每个特征的值只留下一个
    for value in uniqueValue:                                  #循环获取特征的值
        subLables = lables[:]                                  #将删除最优特征的标签列表赋值给subLables
        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet,bestFeat,value), subLables)    #递归调用数生成函数,递归生成树的节点
    return myTree                                             #返回特征的树字典结构表达式

下面是创建决策树的完整代码:

from math import log
import operator
def createDataSet():
    dataSet = [['青年', '否', '否', '一般',    'no'],
            [   '青年', '否', '否', '好',      'no'],
            [   '青年', '是', '否', '好',     'yes'],
            [   '青年', '是', '是', '一般',   'yes'],
            [   '青年', '否', '否', '一般',    'no'],
            [   '中年', '否', '否', '一般',    'no'],
            [   '中年', '否', '否', '好',      'no'],
            [   '中年', '是', '是', '好',     'yes'],
            [   '中年', '否', '是', '非常好', 'yes'],
            [   '中年', '否', '是', '非常好', 'yes'],
            [   '老年', '否', '是', '非常好', 'yes'],
            [   '老年', '否', '是', '好',     'yes'],
            [   '老年', '是', '否', '好',     'yes'],
            [   '老年', '是', '否', '非常好', 'yes'],
            [   '老年', '否', '否', '一般',     'no']]
    labels = ['年龄', '有工作', '有自己的房子', '信贷情况']
    return dataSet,labels


"""
DataSet:给定的数据集
Function:计算给定数据集的香浓熵
return:返回的是计算好的香浓熵
"""
def ShannonEnt(dataSet):
    len_dataSet = len(dataSet)     #样本数据的个数
    label_class = {}               #用来记录每个样本类别的个数
    Ent = 0.0                      #用来存储经验熵
    for item in dataSet:          #循环读入实例
        if item[-1] not in label_class.keys():       #如果存储类别的的字典内没有现在的类别,那么就创建一个以当前类别为key值的元素
            label_class[item[-1]] = 0                 #并将其value值赋值为0
        label_class[item[-1]] += 1                    #如果字典内已经存在此类别,那么将其value加 1,即当前类别的个数加一
    for lable in label_class:                         #从字典内循环获取所有的类别
        P = float(label_class[lable]) / len_dataSet   #计算当前类别占总样本数据的比例,即当前类别出现的概率
        Ent -= P * log(P, 2)        #计算所有类别的香浓熵
    return Ent


"""
dataSet: 给定的数据集
axis: 给定的特征的索引值
value: 对应索引的值
new_dataset: 根据给定的特征划分的新数据
Function:按照给定的特征将数据集分类成新的数据集
"""
def splitDataSet(dataSet, axis, value):
    new_dataset = []
    for item in dataSet:                            #循环读入数据集的每一行数据
        if item[axis] == value:                     #如果是我们需要的特征的数据就将其存放到新的数组中
            templet_set = item[:axis]               #中间变量,用于存放获取的特征变量所在行的其他数据
            templet_set.extend(item[axis+1:])       #a=[1,2,3], b=[4,5,6]   a.extend(b)=[1, 2, 3, 4, 5, 6]
            new_dataset.append(templet_set)         #a=[1,2,3], b=[4,5,6]   a.append(b)=[1, 2, 3, [4,5,6] ]
    return new_dataset


"""
dataSet: 输入的数据集
Function: 选择信息增益最大的特征
return: 返回的是信息增益最大的特征在DataSet中的列索引值
"""
def chooseBestFeature(dataSet):
    len_lables = len(dataSet[0]) - 1     #获取数据集的特征总数。减去的是数据集的类别
    base_Ent = ShannonEnt(dataSet)  #获得总数据集的香农熵
    base_InfoGain = 0.0             #用于记录当前最佳信息增益
    best_lables = -1                #用于记录获得最佳信息增益的特征的索引值
    for i in range(len_lables):    #获取每个特征相应的香农熵
        lable_list = [items[i] for items in dataSet]  #利用列表生成式获取相应特征下的分类,item表示为dataSet的单行数据,item[i]表示对应数据的具体数值
        unique_value = set(lable_list)  #利用集合获得唯一的数据特征,set跟数学中的集合概念类似,里面没有重复的元素
        new_Ent = 0.0                 #用于存放当前子类数据集的经验条件熵
        for value in unique_value:   #获取单个特征值下的对应值例如:青年,中年, 老年
            sub_dataset = splitDataSet(dataSet, i, value)  #按照当前的特征值将数据集进行划分
            prob = len(sub_dataset) / float(len(dataSet))  #获得当前特征的数据占总数据的比例,即概率
            new_Ent += prob * ShannonEnt(sub_dataset)      #获得当前类别的经验条件熵
        info_Gain = base_Ent - new_Ent          #获得当前的信息增益
        #print("第",i,"个特征的信息增益为 ",info_Gain)
        if(info_Gain > base_InfoGain):
            base_InfoGain = info_Gain           #如果遇见更好的信息增益,就将更好的信息增益赋值给best_InfoGain,并且记录下当前信息增益的特征值的索引值
            best_lables = i
    #print("最好的特征索引值为:",best_lables)
    return best_lables

"""
classlist:给定的数据字典
function:如果决策树仍然不能正确分类,那么就采取举手表达的措施选择类数最大的类别
"""
def majorityCnt(classlist):
    classCount = {}                             #创建一个字典,用于存放最大的类别
    for vote in classlist:
        if vote not in classCount.keys():
            classCount[vote] = 0
        classCount[vote] += 1
    sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]

"""
dataSet:给定的数据集
lables:给定的标签
return:返回的是构造好的决策树
"""
def createTree(dataSet,lables):
    class_list = [example[-1]  for example in dataSet]         #获取数据集的类别
    if class_list.count(class_list[0]) == len(class_list):      #如果数据集都是一种类别的话,那么就返回这个类别
        return class_list[0]
    if len(dataSet[0]) == 1:                                    #如果遍历完所有数据任有数据不能正确分类,那么就采用举手表决的方式,选择数据类最大的一个类别
        return majorityCnt(class_list)
    bestFeat = chooseBestFeature(dataSet)                      #获取最佳特征的索引值
    bestFeatLabel = lables[bestFeat]                           #根据索引值在标签列表里面获得标签的名称
    myTree = {bestFeatLabel:{}}                                #创建一个字典,这个字典用于存放决策树的数据结构
    del(lables[bestFeat])                                      #获得了我们需要的标签之后,就将标签从标签列表里面删除,防止产生死循环
    featValue = [example[bestFeat] for example in dataSet]     #从数据集获得对应标签的所有数据,即最好的特征数据的值
    uniqueValue = set(featValue)                               #利用集合将重复的特征数据的值删除。每个特征的值只留下一个
    for value in uniqueValue:                                  #循环获取特征的值
        subLables = lables[:]                                  #将删除最优特征的标签列表赋值给subLables
        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet,bestFeat,value), subLables)    #递归调用数生成函数,递归生成树的节点
    return myTree                                             #返回特征的树字典结构表达式


if __name__ == "__main__":
    dataSet,label = createDataSet()
    myTree = createTree(dataSet,label)
    print("决策树的字典表达式:")
    print(myTree)

下面是创建出来的决策树

决策树的字典表达式:
{'有自己的房子': {'否': {'有工作': {'否': 'no', '是': 'yes'}}, '是': 'yes'}}

Process finished with exit code 0

二使用决策树进行分类工作

依靠训练数据结构建立了决策树之后,我们可以将它应用于实际数据的分类。在执行数据分类时,需要使用决策树以及用于构建决策树的标签向量。然后程序比较测试数据与决策树上的数值,递归执行该过程直到进入叶子结点;最后将测试数据定义为叶子结点所属的类型。

为了验证算法的实际效果,需要编写一段测试程序:


"""
inputTree:输入我们构建好的树
featLabels:数据集的标签
testVec:列表,包含树的节点
return:返回的是叶子结点
function:用于测试决策树是否合格
"""
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

然后我们需要将结构树进行持久化操作,将结构树以二进制方式写入文本,下次使用时,可以直接从文本读取结构树,而不必重新运行所有程序来求取结构树。我们使用  pickle 来进行决策树的持久化以及读取操作:

import pickle
"""
function:将决策树持久化
"""
def storeTree(inputTree, filename):
    with open(filename, 'wb') as f:     #以二进制方式写入数据
        pickle.dump(inputTree,f)

"""
function:读取持久化的决策树
"""
def grabTree(filename):
    with open(filename,'rb') as r:       #以二进制方式读取数据
        return pickle.load(r)

然后我们可以来检验一下是否能够正常使用我们的决策树,我们将会利用决策树来进行眼镜案例的分类,首先我们需要验证我们是否正常将决策树持久化了:

from math import log
import operator
import pickle


def createDataSet():
    dataSet = [['青年', '否', '否', '一般',    'no'],
            [   '青年', '否', '否', '好',      'no'],
            [   '青年', '是', '否', '好',     'yes'],
            [   '青年', '是', '是', '一般',   'yes'],
            [   '青年', '否', '否', '一般',    'no'],
            [   '中年', '否', '否', '一般',    'no'],
            [   '中年', '否', '否', '好',      'no'],
            [   '中年', '是', '是', '好',     'yes'],
            [   '中年', '否', '是', '非常好', 'yes'],
            [   '中年', '否', '是', '非常好', 'yes'],
            [   '老年', '否', '是', '非常好', 'yes'],
            [   '老年', '否', '是', '好',     'yes'],
            [   '老年', '是', '否', '好',     'yes'],
            [   '老年', '是', '否', '非常好', 'yes'],
            [   '老年', '否', '否', '一般',     'no']]
    labels = ['年龄', '有工作', '有自己的房子', '信贷情况']
    return dataSet,labels


"""
DataSet:给定的数据集
Function:计算给定数据集的香浓熵
return:返回的是计算好的香浓熵
"""
def ShannonEnt(dataSet):
    len_dataSet = len(dataSet)     #样本数据的个数
    label_class = {}               #用来记录每个样本类别的个数
    Ent = 0.0                      #用来存储经验熵
    for item in dataSet:          #循环读入实例
        if item[-1] not in label_class.keys():       #如果存储类别的的字典内没有现在的类别,那么就创建一个以当前类别为key值的元素
            label_class[item[-1]] = 0                 #并将其value值赋值为0
        label_class[item[-1]] += 1                    #如果字典内已经存在此类别,那么将其value加 1,即当前类别的个数加一
    for lable in label_class:                         #从字典内循环获取所有的类别
        P = float(label_class[lable]) / len_dataSet   #计算当前类别占总样本数据的比例,即当前类别出现的概率
        Ent -= P * log(P, 2)        #计算所有类别的香浓熵
    return Ent


"""
dataSet: 给定的数据集
axis: 给定的特征的索引值
value: 对应索引的值
new_dataset: 根据给定的特征划分的新数据
Function:按照给定的特征将数据集分类成新的数据集
"""
def splitDataSet(dataSet, axis, value):
    new_dataset = []
    for item in dataSet:                            #循环读入数据集的每一行数据
        if item[axis] == value:                     #如果是我们需要的特征的数据就将其存放到新的数组中
            templet_set = item[:axis]               #中间变量,用于存放获取的特征变量所在行的其他数据
            templet_set.extend(item[axis+1:])       #a=[1,2,3], b=[4,5,6]   a.extend(b)=[1, 2, 3, 4, 5, 6]
            new_dataset.append(templet_set)         #a=[1,2,3], b=[4,5,6]   a.append(b)=[1, 2, 3, [4,5,6] ]
    return new_dataset


"""
dataSet: 输入的数据集
Function: 选择信息增益最大的特征
return: 返回的是信息增益最大的特征在DataSet中的列索引值
"""
def chooseBestFeature(dataSet):
    len_lables = len(dataSet[0]) - 1     #获取数据集的特征总数。减去的是数据集的类别
    base_Ent = ShannonEnt(dataSet)  #获得总数据集的香农熵
    base_InfoGain = 0.0             #用于记录当前最佳信息增益
    best_lables = -1                #用于记录获得最佳信息增益的特征的索引值
    for i in range(len_lables):    #获取每个特征相应的香农熵
        lable_list = [items[i] for items in dataSet]  #利用列表生成式获取相应特征下的分类,item表示为dataSet的单行数据,item[i]表示对应数据的具体数值
        unique_value = set(lable_list)  #利用集合获得唯一的数据特征,set跟数学中的集合概念类似,里面没有重复的元素
        new_Ent = 0.0                 #用于存放当前子类数据集的经验条件熵
        for value in unique_value:   #获取单个特征值下的对应值例如:青年,中年, 老年
            sub_dataset = splitDataSet(dataSet, i, value)  #按照当前的特征值将数据集进行划分
            prob = len(sub_dataset) / float(len(dataSet))  #获得当前特征的数据占总数据的比例,即概率
            new_Ent += prob * ShannonEnt(sub_dataset)      #获得当前类别的经验条件熵
        info_Gain = base_Ent - new_Ent          #获得当前的信息增益
        #print("第",i,"个特征的信息增益为 ",info_Gain)
        if(info_Gain > base_InfoGain):
            base_InfoGain = info_Gain           #如果遇见更好的信息增益,就将更好的信息增益赋值给best_InfoGain,并且记录下当前信息增益的特征值的索引值
            best_lables = i
    #print("最好的特征索引值为:",best_lables)
    return best_lables

"""
classlist:给定的数据字典
function:如果决策树仍然不能正确分类,那么就采取举手表达的措施选择类数最大的类别
"""
def majorityCnt(classlist):
    classCount = {}                             #创建一个字典,用于存放最大的类别
    for vote in classlist:
        if vote not in classCount.keys():
            classCount[vote] = 0
        classCount[vote] += 1
    sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]

"""
dataSet:给定的数据集
lables:给定的标签
return:返回的是构造好的决策树
"""
def createTree(dataSet,lables):
    class_list = [example[-1]  for example in dataSet]         #获取数据集的类别
    if class_list.count(class_list[0]) == len(class_list):      #如果数据集都是一种类别的话,那么就返回这个类别
        return class_list[0]
    if len(dataSet[0]) == 1:                                    #如果遍历完所有数据任有数据不能正确分类,那么就采用举手表决的方式,选择数据类最大的一个类别
        return majorityCnt(class_list)
    bestFeat = chooseBestFeature(dataSet)                      #获取最佳特征的索引值
    bestFeatLabel = lables[bestFeat]                           #根据索引值在标签列表里面获得标签的名称
    myTree = {bestFeatLabel:{}}                                #创建一个字典,这个字典用于存放决策树的数据结构
    del(lables[bestFeat])                                      #获得了我们需要的标签之后,就将标签从标签列表里面删除,防止产生死循环
    featValue = [example[bestFeat] for example in dataSet]     #从数据集获得对应标签的所有数据,即最好的特征数据的值
    uniqueValue = set(featValue)                               #利用集合将重复的特征数据的值删除。每个特征的值只留下一个
    for value in uniqueValue:                                  #循环获取特征的值
        subLables = lables[:]                                  #将删除最优特征的标签列表赋值给subLables
        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet,bestFeat,value), subLables)    #递归调用数生成函数,递归生成树的节点
    return myTree                                             #返回特征的树字典结构表达式


"""
inputTree:输入我们构建好的树
featLabels:数据集的标签
testVec:列表,包含树的节点
return:返回的是叶子结点
function:用于测试决策树是否合格
"""
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

"""
function:将决策树持久化
"""
def storeTree(inputTree, filename):
    with open(filename, 'wb') as f:     #以二进制方式写入数据
        pickle.dump(inputTree,f)

"""
function:读取持久化的决策树
"""
def grabTree(filename):
    with open(filename,'rb') as r:       #以二进制方式读取数据
        return pickle.load(r)


if __name__ == "__main__":
    dataSet, label = createDataSet()
    myTree = createTree(dataSet,label)
    filename = 'C:/Users/lpp/Desktop/Tree.txt'
    storeTree(myTree, filename)
    load_tree = grabTree(filename)
    print(load_tree)

然后输出我们的结果:

{'有自己的房子': {'否': {'有工作': {'否': 'no', '是': 'yes'}}, '是': 'yes'}}

Process finished with exit code 0

这说明我们可以正常使用我们构建的决策树了,然后我们可以看一下我们保存的决策树的具体内容:一堆乱码,因为我们使用的是二进制存储。

三使用决策树预测隐形眼镜的类型

通过这两节内容,我们已经构建了一个可以正常使用的决策树模型了,那么我们现在就利用已经构建好的决策树模型来预测隐形眼镜的分类,数据集以及python代码连接

  https://github.com/Jack-honor/Machine-Learning/tree/master/%E5%86%B3%E7%AD%96%E6%A0%91


from math import log
import operator
import pickle

"""
DataSet:给定的数据集
Function:计算给定数据集的香浓熵
return:返回的是计算好的香浓熵
"""
def ShannonEnt(dataSet):
    len_dataSet = len(dataSet)     #样本数据的个数
    label_class = {}               #用来记录每个样本类别的个数
    Ent = 0.0                      #用来存储经验熵
    for item in dataSet:          #循环读入实例
        if item[-1] not in label_class.keys():       #如果存储类别的的字典内没有现在的类别,那么就创建一个以当前类别为key值的元素
            label_class[item[-1]] = 0                 #并将其value值赋值为0
        label_class[item[-1]] += 1                    #如果字典内已经存在此类别,那么将其value加 1,即当前类别的个数加一
    for lable in label_class:                         #从字典内循环获取所有的类别
        P = float(label_class[lable]) / len_dataSet   #计算当前类别占总样本数据的比例,即当前类别出现的概率
        Ent -= P * log(P, 2)        #计算所有类别的香浓熵
    return Ent


"""
dataSet: 给定的数据集
axis: 给定的特征的索引值
value: 对应索引的值
new_dataset: 根据给定的特征划分的新数据
Function:按照给定的特征将数据集分类成新的数据集
"""
def splitDataSet(dataSet, axis, value):
    new_dataset = []
    for item in dataSet:                            #循环读入数据集的每一行数据
        if item[axis] == value:                     #如果是我们需要的特征的数据就将其存放到新的数组中
            templet_set = item[:axis]               #中间变量,用于存放获取的特征变量所在行的其他数据
            templet_set.extend(item[axis+1:])       #a=[1,2,3], b=[4,5,6]   a.extend(b)=[1, 2, 3, 4, 5, 6]
            new_dataset.append(templet_set)         #a=[1,2,3], b=[4,5,6]   a.append(b)=[1, 2, 3, [4,5,6] ]
    return new_dataset


"""
dataSet: 输入的数据集
Function: 选择信息增益最大的特征
return: 返回的是信息增益最大的特征在DataSet中的列索引值
"""
def chooseBestFeature(dataSet):
    len_lables = len(dataSet[0]) - 1     #获取数据集的特征总数。减去的是数据集的类别
    base_Ent = ShannonEnt(dataSet)  #获得总数据集的香农熵
    base_InfoGain = 0.0             #用于记录当前最佳信息增益
    best_lables = -1                #用于记录获得最佳信息增益的特征的索引值
    for i in range(len_lables):    #获取每个特征相应的香农熵
        lable_list = [items[i] for items in dataSet]  #利用列表生成式获取相应特征下的分类,item表示为dataSet的单行数据,item[i]表示对应数据的具体数值
        unique_value = set(lable_list)  #利用集合获得唯一的数据特征,set跟数学中的集合概念类似,里面没有重复的元素
        new_Ent = 0.0                 #用于存放当前子类数据集的经验条件熵
        for value in unique_value:   #获取单个特征值下的对应值例如:青年,中年, 老年
            sub_dataset = splitDataSet(dataSet, i, value)  #按照当前的特征值将数据集进行划分
            prob = len(sub_dataset) / float(len(dataSet))  #获得当前特征的数据占总数据的比例,即概率
            new_Ent += prob * ShannonEnt(sub_dataset)      #获得当前类别的经验条件熵
        info_Gain = base_Ent - new_Ent          #获得当前的信息增益
        #print("第",i,"个特征的信息增益为 ",info_Gain)
        if(info_Gain > base_InfoGain):
            base_InfoGain = info_Gain           #如果遇见更好的信息增益,就将更好的信息增益赋值给best_InfoGain,并且记录下当前信息增益的特征值的索引值
            best_lables = i
    #print("最好的特征索引值为:",best_lables)
    return best_lables

"""
classlist:给定的数据字典
function:如果决策树仍然不能正确分类,那么就采取举手表达的措施选择类数最大的类别
"""
def majorityCnt(classlist):
    classCount = {}                             #创建一个字典,用于存放最大的类别
    for vote in classlist:
        if vote not in classCount.keys():
            classCount[vote] = 0
        classCount[vote] += 1
    sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]

"""
dataSet:给定的数据集
lables:给定的标签
return:返回的是构造好的决策树
"""
def createTree(dataSet,lables):
    class_list = [example[-1]  for example in dataSet]         #获取数据集的类别
    if class_list.count(class_list[0]) == len(class_list):      #如果数据集都是一种类别的话,那么就返回这个类别
        return class_list[0]
    if len(dataSet[0]) == 1:                                    #如果遍历完所有数据任有数据不能正确分类,那么就采用举手表决的方式,选择数据类最大的一个类别
        return majorityCnt(class_list)
    bestFeat = chooseBestFeature(dataSet)                      #获取最佳特征的索引值
    bestFeatLabel = lables[bestFeat]                           #根据索引值在标签列表里面获得标签的名称
    myTree = {bestFeatLabel:{}}                                #创建一个字典,这个字典用于存放决策树的数据结构
    del(lables[bestFeat])                                      #获得了我们需要的标签之后,就将标签从标签列表里面删除,防止产生死循环
    featValue = [example[bestFeat] for example in dataSet]     #从数据集获得对应标签的所有数据,即最好的特征数据的值
    uniqueValue = set(featValue)                               #利用集合将重复的特征数据的值删除。每个特征的值只留下一个
    for value in uniqueValue:                                  #循环获取特征的值
        subLables = lables[:]                                  #将删除最优特征的标签列表赋值给subLables
        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet,bestFeat,value), subLables)    #递归调用数生成函数,递归生成树的节点
    return myTree                                             #返回特征的树字典结构表达式


"""
inputTree:输入我们构建好的树
featLabels:数据集的标签
testVec:列表,包含树的节点
return:返回的是叶子结点
function:用于测试决策树是否合格
"""
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

"""
function:将决策树持久化
"""
def storeTree(inputTree, filename):
    with open(filename, 'wb') as f:     #以二进制方式写入数据
        pickle.dump(inputTree,f)

"""
function:读取持久化的决策树
"""
def grabTree(filename):
    with open(filename,'rb') as r:       #以二进制方式读取数据
        return pickle.load(r)


if __name__ == "__main__":
    filename = 'C:/Users/lpp/Desktop/lenses.txt'
    with open(filename) as f:
        lenses = [inst.strip().split('\t') for inst in f.readlines()]
        lensesLabels = ['age', 'prescript', 'astigmatic', 'tearRate']
        lensesTree = createTree(lenses, lensesLabels)
    print(lensesTree)

代码跟上面几乎没有什么不同,唯一不同的地方就是更该了主程序,将数据的读取换成从文件读取

结果如下:

{'tearRate': {'normal': {'astigmatic': {'yes': {'prescript': {'myope': 'hard', 'hyper': {'age': {'presbyopic': 'no lenses', 'young': 'hard', 'pre': 'no lenses'}}}}, 'no': {'age': {'presbyopic': {'prescript': {'myope': 'no lenses', 'hyper': 'soft'}}, 'young': 'soft', 'pre': 'soft'}}}}, 'reduced': 'no lenses'}}

Process finished with exit code 0

 

 

 

 

 

 

 

 

猜你喜欢

转载自blog.csdn.net/lpp5406813053/article/details/85246901
今日推荐