决策树判断海洋生物是否为鱼类

"""
优点:计算复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可以处理不相关特征数据
缺点:可能会产生过度匹配问题
运用数据类型:数值型和标称型
决策树的一般流程:
    1.收集数据:可以使用任何方法
    2.准备数据:树构造算法只适用于标称型数据,因此数值型数据必须离散化
    3.分析数据:可以使用任何方法,构造树完成后,我们应该检查图形是否符合预期
    4.训练算法:构造树的数据结构
    5.测试算法:使用经验树计算错误率
    6.使用算法:此步骤可以适用于任何监督学习算法,而使用决策树可以更好地理解数据的内在含义
"""
from math import log
import operator


def calcShannonEnt(dataSet):
    """
    该方法计算给定数据集的香农熵
    :param dataSet:
    :return:
    """
    numEntries = len(dataSet)  # 计算数据集中实例的总数
    labelCounts = {}  # 字典dict

    # 为所有可能分类创建字典
    for featVec in dataSet:
        currentLabel = featVec[-1]  # -1下标可获取最后一个元素的值 yes yes no no no
        if currentLabel not in labelCounts.keys():
            labelCounts[currentLabel] = 0
        labelCounts[currentLabel] += 1

    # 计算香农熵,以2为底求对数
    shannonEnt = 0.0
    for key in labelCounts:
        prob = float(labelCounts[key])/numEntries  # 选择该分类的概率
        shannonEnt -= prob * log(prob, 2)  # 计算香农熵
    return shannonEnt

"""
得到熵之后,我们就可按照获取最大信息增益的方法划分数据集,如何划分数据集以及如何度假来哪个信息增益?
分类算法除了需要测量信息熵,还需要划分数据集,度量划分数据集的熵,以便判断
当前是否正确地划分了数据集,我们将对每个特征划分数据集的结果计算一次信息熵,然后判断
按照哪个特征划分数据集是最好的方式,想象一个分布在二维空间的数据散点图,需要在数据之间画条线
将他们分成两部分,我们应该按照x周还是y轴的
"""


def splitDataSet(dataSet, axis, vallue):
    """
    按照给定的特征划分数据集
    在python中,在函数内部对列表对象的修改,将会影响该列表对象的整个生存周期,
    为了消除这个不良影响,我们需要在函数的开始声明一个新列表对象,因为该函数嗲吗在同一
    数据集上被调用多次,为了不修改原始数据集,我们需要在函数的开始声明一个新的列表对象
    数据集这个列表中的各个元素也是列表,我们要遍历数据集中的每个元素,一旦发现符合要求的值
    则将其添加到新创建的列表中
    :param dataSet:待划分的数据集
    :param axis:划分数据集的特征
    :param vallue:需要返回的特征的值
    :return:
    """
    retDataSet = []  # 创建新的list对象,
    for featVec in dataSet:  # 数据集中的元素仍为列表,featVec的值也是列表
        if featVec[axis] == vallue:  # 如果列表元素的axis位置的元素的值与value相等
            reducedFeatVec = featVec[:axis]  # reducedFeatVec为列表类型,如featVec[:3]取值为下表0-2的值
            reducedFeatVec.extend(featVec[axis+1:])  # 注意extend和append的区别:a.extend(b) = [1, 2, 3, 4, 5, 6]
            retDataSet.append(reducedFeatVec)  # a = [1, 2, 3] b = [4, 5, 6] a.append(b)结果为:[1, 2, 3, [4, 5, 6]]
    return retDataSet

"""
接下来我们将遍历整个数据集,循环计算香农熵和splitDataSet()函数,找到最好的特征
划分方式,熵计算将会告诉我们如何划分数据集是最好的数据组织方式
"""

def chooseBestFeatureToSplit(dataSet):
    """
    选择最好的数据集划分方式
    在函数中调用的数据必须满足一定的要求,第一个要求是,数据必须是一种由列表元素
    组成的列表,而且所有的列表元素都要具有相同的数据长度,第二个要求是,数据的最后一列
    或者每个实例的最后一个元素是当前实例的类别标签,数据集一但满足上述要求,我们就可以在
    函数第一行判断当前数据集包含多少种特征属性。
    :param dataSet:
    :return:
    """
    numFeatures = len(dataSet[0]) - 1  # 判断当前数据集包含多少种特征属性
    baseEntropy = calcShannonEnt(dataSet)  # 计算原始香农熵,保存最初的无序度量值,用于划分完之后的数据集计算的熵值进行比较
    bestInfoGain = 0.0
    bestFeature = -1

    # 该for循环遍历数据集的所有特征
    for i in range(numFeatures):
        featList = [example[i] for example in dataSet]  # 将数据集中所有第i个特征值或者所有可能存在的值写入这个新list中
        uniqueVals = set(featList)  # set:集合数据类型,集合数据类型与列表类型相似,不同之处在于集合类型中的每个值互补相同,从列表中创建集合是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):
    """
    多数表决
    :param classList:
    :return:
    """
    classCount = {}  # 数据字典
    for vote in classList:
        # 如果不存在,就添加到列表中
        if vote not in classCount.keys():
            classCount[vote] = 0
        classCount[vote] += 1  # 字典对象存储了classList中每个类标签出现的频率,利用operator操作键值排序字典,并返回出现次数最多的分类名称
    sortedClassCount = sorted(classCount.iteritems(), key = operator.itemgetter(1), reversed = True)
    return sortedClassCount[0][0]


def createTree(dataSet, labels):
    classList = [example[-1] for example in dataSet]  # 取数据集最后一列元素 

    # 递归函数的第一个停止条件:所有类标签完全相同,则直接返回类标签
    if classList.count(classList[0]) == len(classList):  # count方法返回元素在列表中出现的次数
        return classList[0]

    # 递归函数的第二个停止条件:使用完了所有特征,仍然不能将数据集划分成仅包含唯一类别的分组
    if len(dataSet[0]) == 1:
        return majorityCnt(classList)

    bestFeat = chooseBestFeatureToSplit(dataSet)  # 当前数据集选取的最好特征存储在bestFeat中
    bestFeatLabel = labels[bestFeat]
    myTree = {bestFeatLabel:{}}  # 字典变量存储了树所有的信息
    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


if __name__ == '__main__':

    dataSet = [[1, 1, "yes"],
               [1, 1, "yes"],
               [1, 0, "no"],
               [0, 1, "no"],
               [0, 1, "no"]]
    labels = ["no surfacing", "flippers"]
    myTree = createTree(dataSet, labels)
    print(myTree)
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值