《机器学习实战》学习——决策树算法代码

小游戏:参与游戏的一方在脑海里想某个事物,其他参与者向他提问题,只允许提20个问题,问题的答案也只能用对或错回答。问问题的人通过推断分解,逐步缩小待猜测事物的范围。

决策树的工作原理与此类似,用户输入一系列数据,然后给出游戏的答案。决策树流行的一个很重要的原因是不需要了解机器学习的知识,就能搞明白决策树是如何工作的。

理解决策树的实例:

 《机器学习实战》学习——决策树算法代码_第1张图片

决策树的伪代码:

《机器学习实战》学习——决策树算法代码_第2张图片

 

 《机器学习实战》学习——决策树算法代码_第3张图片

from math import log
import operator


def calcShannonEnt(dataSet):
    """
    计算频率来当做类别的概率;计算香农熵
    labelCounts是一个字典,键是label名称,对应的值是该label的数量
    创建labelCounts的目的是计算每个label的频率,以此来代替概率
    """
    numEntries = len(dataSet)
    labelCounts = {}
    for featVec in dataSet:  # 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)  # log函数的用法;计算香农熵!!
    return shannonEnt


# 下面的函数要计算条件熵,先找出子集合,再利用calcShannonEnt来计算条件熵

def splitDataSet(dataSet, axis, value):
    """
    :param dataSet: 待划分的数据集
    :param axis: 划分数据集的特征(第一列是一个特征,第二列是另一个特征,...,选取一列即一个特征)
    :param value: 需要返回的特征的值(判断选取的这列特征是不是我们想要的特征的值,特征的值一般为是否这种)
    :return:我们挑选数据集中特征axis的值为value(特征的值)的元素放在新建的列表中
    """
    retDataSet = []
    for featVec in dataSet:
        if featVec[axis] == value:
            reducedFeatVec = featVec[:axis]  # 因为axis=0,而切片又不能包括最后一个值,所以这是个空列表
            reducedFeatVec.extend(featVec[axis + 1:])
            # 这两步就是将除去了指定特征axis的元素重新排成列表
            retDataSet.append(reducedFeatVec)  # 把整合后的列表当成元素存在新的子集合中
    return retDataSet


# 利用信息增益找出最佳划分特征
def chooseBestFeatureToSplit(dataSet):
    numFeatures = len(dataSet[0]) - 1  # 特征的个数
    baseEntropy = calcShannonEnt(dataSet)  # 计算划分前的熵
    bestInfoGain = 0.0
    bestFeature = -1
    """
    下面的第一个for循环是为了找出每一列特征有哪些不同的值,并且遍历了所有的特征;第二个for循环利用刚刚找出的特征的不同值,计算条件熵;每一个特征都需要计算信息增益,所以遍历
    所有特征的是大循环,且需要if循环来将每个特征的信息增益进行比较,最后得到信息增益最大的即为最佳划分特征
    """
    for i in range(numFeatures):
        featList = [example[i] for example in dataSet]  # 把数据集中的第i+1列特征的值都存在此列表中
        uniqueVals = set(featList)  # 该特征值的种类
        # set() 函数创建一个无序不重复元素集,可进行关系测试,删除重复数据
        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):
    """
    字典对象存储了classList中每个类标签出现的频率,最后利用operator操作键值排序字典
    并返回出现次数最多的分类名称
    """
    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)  # 排序(降序排列)
    # classCount.items()是将字典写成有两个元素的元组的形式,这两个元素分别是两个列表,一个是键列表,一个是值列表;operator.itemgetter(1)是读取元组中索引值为1的元素即列表
    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)
    """
    如果使用完了所有特征,即特征没了只剩label,所以长度为1,但仍然不能将数据集划分成仅包含唯一类别的分组,
    则这一部分使用函数majorityCnt,挑选出现次数最多的类别作为返回值(第二个停止条件)
    """
    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[:]  # 复制标签列表到sub~中,已经是删掉最优特征的标签列
        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), subLabels)
        # 因为有前面两个if,所以如果分类都一样的话,输出的就是yes or no;结果中出现的0,1其实是上一层字典的键值,每个键值又是下一层字典的键,对应的键值是新的字典
    return myTree


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

count函数的用法:

list.count(obj)列表中某元素出现的次数

str.count(sub, start= 0,end=len(string))字符串中某字符出现的次数

extend与append的区别:

a=[1,2,3]
b=[4,5,6]
a.append(b)
print(a)
[1, 2, 3, [4, 5, 6]]
a=[1,2,3]
b=[4,5,6]
a.extend(b)
print(a)
[1, 2, 3, 4, 5, 6]

你可能感兴趣的:(机器学习实战,python,机器学习,决策树,数据挖掘)