决策树(《Machine Learning in Action》笔记)

优点:计算复杂度不高,输出结果易于理解,对于中间值的缺失不敏感,可以处理不相关特征数据。
缺点:可能过拟合
适合数据类型:数值型(必须离散化)和标称型

在构造决策树时,需要解决的第一个问题是:数据集上哪个特征(如何选择)在划分数据(大的原则:将无序的数据变得更加有序)时起决定性作用。

P.S.
1. 信息增益:在划分数据集之前之后信息发生的变化(InfoGain越高,特征越好)
2. 熵:信息的期望值

from math import log
import operator

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):
    ''' 划分数据集,将符合特征的数据抽取出来 输入:数据集,特征值得位置,特征值 输出:抽取出来的符合特征的数据集 '''
    retDataSet = []
    for featVec in dataSet:
        if featVec[axis] == value:
            reduceFeatVec = featVec[:axis]
            reduceFeatVec.extend(featVec[axis + 1 :])
            retDataSet.append(reduceFeatVec)
    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]      #列表推导
        uniqueVals = set(featList)      #从列表中创建集合是Python语言得到列表中唯一元素值得最快方法
        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]
    if classList.count(classList[0]) == len(classList):     #如果类别完全相同
        return classList[0]
    if len(dataSet[0]) == 1:        #如果遍历完所有特征
        return majorityCnt(classList)
    bestFeat = chooseBestFeatureToSplit(dataSet)
    bestFeatLabel = labels[bestFeat]        #属性的index
    myTree = {bestFeatLabel:{}}     #myTree中存储了树的所有信息
    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)      #用index方法查找当前列表中第一个匹配firstStr变量的元素的位置
    for key in secondDict.keys():       #遍历整棵树
        if testVec[featIndex] == key:       #比较树节点中的值和testVec变量中的值
            if type(secondDict[key]).__name__ == 'dict':
                classLabel = classify(secondDict[key], featLabels, testVec)
            else:
                classLabel = secondDict[key]        #如果到达叶节点,则返回当前节点的分类标签
    return classLabel            
ID3算法并不完美,尽管可以通过量化的方法将数值型数据转化为标称型数值,但是若存在太多的特征划分,此算法仍然会面临其他问题。

后记:这一节本来应该昨天完成的,结果买了张电影票跑去看《疯狂动物城》了,动画电影,看完心里莫名有一种感动。好啦,继续学习。

你可能感兴趣的:(机器学习)