机器学习实战之决策树的构建过程_代码注释

#-*- coding: UTF-8 -*-
from math import log
import operator

def calcShannoEnt(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 createDataset():#定义一个数据集,格式为:特征1,特征2,····,特征n,类别,使用tab分隔符分隔
    dataSet = [[1,1,'yes'],
               [1,1,'yes'],
               [1,0,'no'],
               [0,1,'no'],
               [0,1,'no'],]
    labels = ['no surfacing','flippers']
    return dataSet,labels

def splitDataSet(dataSet,axis,value):
    retDataSet = []
    for featVec in dataSet:
        if featVec[axis] == value:
            reducedFeatVec = featVec[:axis]  #该行和下一行的作用是得到去除待划分列的列表(被划分的列已经不再需要,去除)
            reducedFeatVec.extend(featVec[axis+1:])#扩展
            retDataSet.append(reducedFeatVec)#添加
    return retDataSet

def chooseBestFeatureToSplit(dataSet):#选择最优划分方式,并返回最优划分方式的索引值
    numFeatures = len(dataSet[0]) - 1#减去类别列
    baseEntropy = calcShannoEnt(dataSet)#计算原始香农熵
    bestInfoGain = 0.0; bestFeature = -1#初始化最大信息增益和最佳特征索引
    for i in range(numFeatures):
        featList = [example[i] for example in dataSet]
        uniqueVals = set(featList)#计算按当前特征划分时,当前特征的取值,set是集合数据类型,元素唯一
        newEntropy = 0.0#香农熵初始化
        for value in uniqueVals:#循环计算香农熵
            subDataSet = splitDataSet(dataSet,i,value)
            prob = len(subDataSet) / float(len(dataSet))#当前划分方式占比
            newEntropy += prob * calcShannoEnt(subDataSet)#香农熵之和,即按当前特征取值的香农熵
        infoGain = bestInfoGain - 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]#取出数据集中的类别值,放入列表
#两个if为递归停止的两个条件,即1可以确定出类别或2特征用尽,必须确定出类别,递归停止,其中2调用投票函数
    if classList.count(classList[0]) == len(classList):#count计算列表中第一个元素的个数,若与列表长度相等,则说明当前可以确定出类别,则返回其值
        return classList[0]
    if len(dataSet[0]) == 1:#递归停止的第二个条件,即判断是否所有特征值已经用完,可判断数据集中列表元素是否唯一,即只剩下类别元素,(dataset中的元素是列表)
        return majorityCnt(classList)
    bestFeat = chooseBestFeatureToSplit(dataSet)#选出划分最佳特征值的索引值
    bestFeatLabel = labels[bestFeat]#通过索引值找出当前划分特征的名称(索引值只能找出该特征处于数据集中的哪一列)
    myTree = {bestFeatLabel:{}}#此步开始构建决策��,在之后的递归中,若前一步的两个if判断条件不能判断出类别,则需用此步继续构建子节点,直到满足递归停止条件
    del(labels[bestFeat])#由于接下来会用到splitdataset函数,此函数会删除被划分的特征列(ID3算法会消耗特征)为与新的数据集保持一致,该部从特征列表中删除当前最优划分特征
    featValues = [example[bestFeat] for example in dataSet]#取出当前最优特征的所有值,放入列表
    uniqueVals = set(featValues)#计算无重复特征值个数,即当前特征的取值个数
    for value in uniqueVals:#1此步最关键,遍历当前最优特征的取值,并开始递归计算,即反复划分数据集,重复以上步骤,直到能够确定类别,停止递归
        subLabels = labels[:]#2为了保证调用时不改变原始列表的内容
        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet,bestFeat,value),subLabels)#3注意在循环时,等其中一个value完全递归完成时,
                                                                                                # 下一个value才开始递归计算
    return myTree

def classify (inputTree,featLabels,testVec):#使用决策��分类,输入分别为:树模型,特征集,待分类特征
    firstStr = inputTree.keys()[0]#取��的第一个键值,即第一个特征值(第一个节点)
    secondDict = inputTree[firstStr]#二级��,即上一节点的子节点
    featIndex = featLabels.index(firstStr)#通过index函数找出特征集中符合第一个特征 的 特征 的索引值 赋给featIndex
    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

def storetree(inputTree,filename):#用pickle模块将树模型存储在硬盘上,输入为树模型,要保存为的文件名称
    import pickle#pickle提供类一中简单持久化的功能
    fw = open(filename,'w')
    pickle.dump(inputTree,fw)
    fw.close()

def grabTree(filename):
    import pickle
    fr = open(filename)
    return pickle.load(fr)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值