决策树

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

缺点:可能会产生过度匹配的问题

使用数据类型:数值型和标称型。

#计算数据集的熵
from math import log         #导入log函数
def calcShannonEnt(dataSet):
    numEntries = len (dataSet) #得到数据集的行数
    # print(numEntries)     
    labelCounts = {}
    for featVec in dataSet:    #提取数据集每一行的特征向量
        currentLabel = featVec[-1] #获取特征向量的最后一列标签
        print(currentLabel)
        if currentLabel not in labelCounts.keys(): #检测该标签是否存在与字典的关键字
            labelCounts[currentLabel] = 0    #将当前标签和0键值存入字典中
            print(currentLabel)
        labelCounts[currentLabel] +=1 #若存在,则对当前标签对应的键值加1
        print(labelCounts)       #print函数仅作为测试用,可删去
    Ent = 0      #初始化熵
    for key in labelCounts: #统计数据集中所有的分类
        prob = float(labelCounts[key])/numEntries  #计算各个类别的概率
        Ent-=prob*log(prob,2) #各个类别的信息期望值
    return Ent
#创建数据集,以供测试
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

注:numPy中数组是下标是从0行0列开始的。如【0】【-1】则表示第0行的最后一列的元素

2,划分数据集,度量划分数据集的熵,以便判断当前是否正确划分数据集。

#按照给定特征划分数据集
def splitDataSet(dataSet,axis,value):
    retDataSet = []    #python语言传递参数列表时,传递的是列表的引用
    for featVec in dataSet:   #提取数据集的每一行的特征向量
        if featVec[axis] == value:  #针对axis特征的取值将数据集划分为不同的分支
            #如果该特征的值为value,将特征向量的0-axis-1列存入列表reducedFeatVec
            reducedFeatVec = featVec[:axis] #
            reducedFeatVec.extend(featVec[axis+1:]) #将特征向量的axis+1~最后一列存入元素
            retDataSet.append(reducedFeatVec)  #将每一行得到的列表整合到一起,成为一个大列表
    return retDataSet

注:extend()和append()函数的区别,

比如: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()函数

3.遍历整个数据集,找到最好的特征划分方式。

#选择最好的数据集划分方式
def choseBestFeatureToSplit(dataSet):
    numFeatures = len(dataSet[0])-1   #得到数据集的列再减一就为特征数目
    baseEntropy = calcShannonEnt(dataSet)  #计算未进行划分的数据集的信息熵
    bestInfoGain = 0;bestFeature = -1   #初始化最优信息增益,最优特征
    for i in range(numFeatures):
        featList = [example[i] for example in dataSet]  #得到特征i的所有特征值(即取每一列的所有值)
        uniqueVals = set(featList)  #利用set集合的性质,元素的唯一性,得到特征i的所有可能值
        newEntropy = 0  
        for value in uniqueVals:  #for循环的目的是以特征i的取值进行划分数据集
            subDataSet = splitDataSet(dataSet,i,value)  #获取特征值value分支包含的数据集
            prob = len(subDataSet)/float(len(dataSet))  #计算特征i取值为value时分子集的比例
            newEntropy  += prob * calcShannonEnt(subDataSet) #计算占比*子集信息熵,并累加得到总熵
        infoGain = baseEntropy - newEntropy   #获得信息增益的公式(见公式有助于理解)
        if(infoGain > bestInfoGain):   #为了得到最大信息增益
            bestInfoGain = infoGain
            bestFeature = i    #相应的保存得到最大增益的特征i
    return bestFeature

注:第7,8行代码的具体意思为:创建唯一的分类列表标签。

    featList = [example[i] for example in dataSet]  #得到特征i的所有特征值(即取每一列的所有值)

得到的结果为:


即提取到了每一列的值到一个列表中(example对myDat数据集每一行循环,example[i]得到每一行指定的元素)

    uniqueVals = set(featList)  #利用set集合的性质,元素的唯一性,得到特征i的所有可能值


4.多数表决原则,确定类标签

#当遍历完所有的特征属性后,类标签仍然不唯一(分支仍有不同标签的实例)
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]   #返回出现次数最多的类标签

5. 创建决策树代码

#创建树的代码
def createTree(dataSet,labels):
    classList = [example[-1] for example in dataSet]  #获取数据集的最后一列的类标签,存入classlist列表
    if classList.count(classList[0]) == len(classList):#通过count函数获取类标签列表中第一类标签的数目
        return classList[0]      #判断数目是否等于列表长度,相同表示所有类标签相同,属于同一类。
    if len(dataSet[0]) == 1:    #遍历完所有的特征属性,此时数据集列为1,即只有类标签列
        return majorityCnt(classList) #多数表决原则,确定类标签
    bestFeat = chooseBestFeatureToSplit(dataSet)  #确定出当前最优的分类特征
    bestFeatLabel = labels[bestFeat]  #在特征标签列表中获取该特征对应的值
    myTree = {bestFeatLabel:{}}  #采用字典嵌套字典的方式,存储分类树的信息
    subLabels = labels[:]       #复制当前特征标签列表,防止改变原始列表的内容
    del(subLabels[bestFeat])    #删除属性列表中当前分类数据集特征
    featValues = [example[bestFeat] for example in dataSet] #获取数据集最优特征所在的列
    uniqueVals = set(featValues)  #采用set集合性质。获取列特征中所有的唯一取值
    for value in uniqueVals:   #遍历每一个唯一的特征值
        
        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet,bestFeat,value),
                                                  subLabels)
    return myTree


注:在每个数据集上递归调用函数createTree(),得到的返回值插入到字典变量中,因此当函数结束时,字典中会嵌套很多代表叶子节点信息的字典数据。

我们看一行循环的第一行代码:subLabels = labels[:], 这行代码复制了类标签,并将其存储到新列表subLabels中。

这是因为python函数参数是列表类型时,是按照引用方式传递的,为了保证每次调用函数时不改变原始列表的值,使用新变量代替。

6. 测试算法

#使用决策树测试分类算法
def classify(inputTree,featLabels,testVec):#完成决策树的构造后,应用在分类任务中
    firstStr = list(inputTree.keys())[0] #找到树的第一个分类特征,或者谁是跟节点
    secondDict = inputTree[firstStr]   #从树中得到该分类特征的分支(即跟节点下的分支)
    
    featIndex = featLabels.index(firstStr)  #根据分类特征的索引找到对应的标称型数据值(根节点对应的索引为0)
    for key in secondDict.keys():      #遍历分类特征所有的取值
       
        if testVec[featIndex] == key: #判断测试实例的第0个特征取值是否等于第第key个子节点
            if type(secondDict[key]).__name__=='dict': #判断该子节点是否为字典类型
                     
                classLabel = classify(secondDict[key],featLabels,testVec)#若为字典型,则从该分支继续遍历分类
            else: #如果是叶子节点,则返回节点取值
                classLabel =secondDict[key]
                
    return classLabel

注:结合实战书图3-6理解(p46)

7. 存储决策树

#使用pickle模块存储决策树
def storeTree(inputTree,filename):  #存决策树
    import pickle    #导入pickle模块
    fw = open(filename,'wb')  #创建一个写的文本文件,若写'w'会报错,改为'wb'
    pickle.dump(inputTree,fw)  #pickle的dump函数将决策树写入文件中
    fw.close()  #关闭写完的文件

def grabTree(filename):  #取决策树操作
    import pickle  
    fr = open(filename,'rb')  #对应于二进制写入数据,‘rb’采用二进制读出数据
    return pickle.load(fr) 
 这里,文件的写入操作为'wb'或'wb+',表示以byte的形式写入数据,相应'rb'以byte形式读入数据
     接下来,我们将通过隐形眼镜数据集构建决策树,从而预测患者需要佩戴的隐形眼镜的类型,步骤如下:
(1)收集数据:文本数据集'lenses.txt'
(2)准备数据:解析tab键分隔开的数据行
(3)分析数据:快速检查数据,确保正确地解析数据内容
(4)训练算法:构建决策树
(5)测试算法:通过构建的决策树比较准确预测出分类结果

(6)算法的分类准确类满足要求,将决策树存储下来,下次需要时读取使用

#使用决策树预测影星眼睛类型
def predictType(filename):
    fr = open(filename)  #打开文本数据
    #将文本数据的每一行按照tab键分割,并存入lenses
    lenses = [inst.strip().split('\t') for inst in fr.readlines()]
    lensesLabels = ['age','prescript','astigmatic','tearRate'] #创建特征标签列表
    lensesTree = createTree(lenses,lensesLabels) #创建决策树
    return lensesTree

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值