python代码构造决策树02

0. 前言

之前的学习过程中,构造决策树的过程以及用其进行预测的过程都是通过调用算法库来实现的,为了更好地理解该算法,将参照《机器学习实战》一书来实现这个过程。

1. 构造决策树

数据

不浮出水面可否生存是否有脚蹼属于鱼类

我们通过两种特征来区分某种动物是否属于鱼类。但是需要思考的是,最开始该如何选取特征,选第一个还是第二个,哪一种选择获得的效果更好。这需要通过计算他们的信息增益来决定。通过计算每个特征值划分数据之后的信息增益,比较计算结果,信息增益更大的就是值得选择的特征。

信息熵(entropy):熵是描述信息的不确定度的,是随机变量不确定度的度量。熵越大,信息的不确定度越大,信息越“混乱”,越不符合决策树分类的需求。

                                                      Ent(D) = - ∑ k = 1 n \sum_{k=1}^n k=1n p k p_k pk l o g 2 log_2 log2 p k p_k pk

                        D:样本集合; p k p_k pk:第k类样本所占的比例(k=1,2,3…n)

计算信息熵(香农熵)

def calShannonEnt(dataSet):
    """计算数据集的香农熵"""
    numEntries = len(dataSet)  # 数据集中实例总数
    labelCounts = {}  # 创建数据字典,键值记录label出现的次数
    for featVec in dataSet:
        currentLable = featVec[-1]
        if currentLable not in labelCounts.keys():
            labelCounts[currentLable] = 0
        labelCounts[currentLable] += 1
        print(featVec, labelCounts)
    shannonEnt = 0.0
    for key in labelCounts:
        prob = float(labelCounts[key])/numEntries  # 计算概率p
        shannonEnt -= prob*log(prob, 2)
    return shannonEnt

测试一下这个函数:需要先创建一个数据集,再调用创建数据集的方法和计算香农熵的方法:

from math import log

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


def calShannonEnt(dataSet):
    """计算数据集的香农熵"""
    numEntries = len(dataSet)  # 数据集中实例总数
    labelCounts = {}  # 创建数据字典,键值记录label出现的次数
    for featVec in dataSet:
        currentLable = featVec[-1]
        if currentLable not in labelCounts.keys():
            labelCounts[currentLable] = 0
        labelCounts[currentLable] += 1
        print(featVec, labelCounts)
    shannonEnt = 0.0
    for key in labelCounts:
        prob = float(labelCounts[key])/numEntries  # 计算概率p
        shannonEnt -= prob*log(prob, 2)
    return shannonEnt


myData, labels = createDataSet()
print("数据集:", myData)
print(calShannonEnt(myData))

输出:

数据集: [[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]
# 这里输出的是数据字典统计label次数的过程,每遍历到一个样本,就根据其所属类别更改label的值
[1, 1, 'yes'] {'yes': 1}
[1, 1, 'yes'] {'yes': 2}
[1, 0, 'no'] {'yes': 2, 'no': 1}
[0, 1, 'no'] {'yes': 2, 'no': 2}
[0, 1, 'no'] {'yes': 2, 'no': 3}
# 计算所得的香农熵
0.9709505944546686

划分数据集

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

测试:

myData, labels = createDataSet()
print("数据集:", myData)
print(splitDataSet(myData, 0, 1))

输出结果:

数据集: [[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]
[[1, 'yes'], [1, 'yes'], [0, 'no']]

选择最好的数据集划分方式

def chooseBestFeatureToSplit(dataSet):
    """选择最好的数据集划分"""
    numFeatures = len(dataSet[0]) - 1  # 特征总个数
    baseEntropy = calShannonEnt(dataSet)  # 计算数据集的信息熵
    bestInfoGain, bestFeature = 0.0, -1  # 最优的信息增益值, 最优的Feature编号
    for i in range(numFeatures):
        featList = [example[i] for example in dataSet]  # 获取各实例第i+1个特征
        uniqueVals = set(featList) 
        newEntropy = 0.0  # 创建一个新的信息熵
        for value in uniqueVals:
            subDataSet = splitDataSet(dataSet, i, value)
            prob = len(subDataSet)/float(len(dataSet))
            newEntropy += prob * calShannonEnt(subDataSet)
        # 比较所有特征中的信息增益,返回最好特征划分的索引值。
        infoGain = baseEntropy - newEntropy
        print('infoGain=', infoGain, 'bestFeature=', i, baseEntropy, newEntropy)
        if (infoGain > bestInfoGain):
            bestInfoGain = infoGain
            bestFeature = i
    return bestFeature

测试:

myData, labels = createDataSet()
print("数据集:", myData)
print("choose", chooseBestFeatureToSplit(myData))

输出结果:

数据集: [[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]
[1, 1, 'yes'] {'yes': 1}
[1, 1, 'yes'] {'yes': 2}
[1, 0, 'no'] {'yes': 2, 'no': 1}
[0, 1, 'no'] {'yes': 2, 'no': 2}
[0, 1, 'no'] {'yes': 2, 'no': 3}
[1, 'no'] {'no': 1}
[1, 'no'] {'no': 2}
[1, 'yes'] {'yes': 1}
[1, 'yes'] {'yes': 2}
[0, 'no'] {'yes': 2, 'no': 1}
infoGain= 0.4199730940219749 bestFeature= 0 0.9709505944546686 0.5509775004326937
[1, 'no'] {'no': 1}
[1, 'yes'] {'yes': 1}
[1, 'yes'] {'yes': 2}
[0, 'no'] {'yes': 2, 'no': 1}
[0, 'no'] {'yes': 2, 'no': 2}
infoGain= 0.17095059445466854 bestFeature= 1 0.9709505944546686 0.8
choose 0

根据输出结果,可以看出,选择0的话,信息熵更大,即是更优的选择。
创建决策树

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]
    # count()统计括号中的值在list中出现的次数
    if classList.count(classList[0]) == len(classList):
        return classList[0]
    if len(dataSet[0]) == 1:
        return majorityCnt(classList)
    bestFeat = chooseBestFeatureToSplit(dataSet)  # 选择最优列,得到最优列对应的label含义
    bestFeatLabel = labels[bestFeat]  # 获取label的名称
    myTree = {bestFeatLabel: {}}
    featValues = [example[bestFeat] for example in dataSet]  # 取出最优列并对它的branch做分类
    uniqueVals = set(featValues)
    for value in uniqueVals:
        subLabels = labels[:]
        # 遍历当前选择特征包含的所有属性值,在每个数据集划分上递归调用函数createTree()
        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), subLabels)
    print(myTree)
    return myTree

测试:

myData, labels = createDataSet()
print("数据集:", myData)
myTree = createTree(myData, labels)

输出结果:

数据集: [[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]
{'no surfacing': {0: 'no', 1: 'yes'}}
{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}

根据结果可以看出,该树的根节点是no surfacing,flippers是第一层被划分的节点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值