机器学习实战-决策树的构造(代码详细解释)

机器学习实战-决策树的构造(代码详细解释)

**决策树的思想:
1.计算原始数据的熵
2.选取数据中的特征进行分类
3.分类以后再求熵,根据对比即可知道那个特征对原始数据的影响最大,此时
就以该特征进行分类(相当于树根据这个特征分叉了)
4.使用递归循环,直到按照最后一个特征分类
5.此时,一棵树就出来了。

from math import log
import operator
# 计算给定数据集的香农熵
def calcShannonEnt(dataSet):
    numEntries = len(dataSet)
    labelCounts = {}#建立一个空字典,准备装原始数据的每种分类的数目,"yes"=是,"no"=否
    for featVec in dataSet:
        currentLabel = featVec[-1]#取是否属于”鱼类“的值,既每组数据的最后一个值,作为标签
        if currentLabel not in labelCounts.keys():#如果这个标签不在创建的字典中,则填加进去
            labelCounts[currentLabel] = 0
        labelCounts[currentLabel] += 1#对该标签对应的值+1
        shannonEnt = 0.000#创建一个变量,用于输出熵
    for key in labelCounts:  #根据求熵的公式进行编程
        prob = float(labelCounts[key])/numEntries #利用for循环,求"是"鱼类占总数据的比例和"否"占总数据的比率
        shannonEnt -= prob * log(prob,2) #求熵公式
    return shannonEnt#
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 splitDataSet(dataSet,axis,value):#分别为数据集,列数,用作分类的数(在这里是0和1)
    retDataSet = []
    reduceFeatVec = []
    for featVec in dataSet:#取出每行数
        if featVec[axis] == value:#假如axis=1,则表示是否这行数的第2个和value相同
            reduceFeatVec = featVec[:axis]#如果相同,则取[0:1]中的数  并赋给空列表reduceFeatVec,书中这个reduceFeatVec列表没有定义
            reduceFeatVec.extend(featVec[axis+1:])#同理   既表示除了这列数的其他列都存到一个列表中 注意extend的用法
            retDataSet.append(reduceFeatVec)#将每行剩下的数据当作一组,分别存入一个列表中
    return retDataSet#  例如:返回第一列是0但又不包括0的数据组

def chooseBestFeatureToSplit(dataSet):#选择最好的数据集划分方式,既用那个特征分类最好呢?
    numFeatures = len(dataSet[0] ) - 1#求数据列的长度-1,因为每行有三个数据,而最后一个数据是分类结果,不能当作特征
    baseEntropy = calcShannonEnt(dataSet) #求的原始数据的熵值,目的是要和划分之后的熵做对比
    bestInfoGain = 0;bestFeature = -1#设置两个变量,第一个用于当作中间变量,第二个用于表示那个变量最好
    for i in range(numFeatures):#一共两个变量,用for循环分别求出根据用每个特征值分类以后的熵
        featList = [example[i] for example in dataSet]#写出每列的数字,并存储到列表中
        uniqueVals = set(featList)#创建集合为{0,1}
        newEntropy = 0.0
        for value in uniqueVals:#集合中一共有0,1两个数 value= 0/1
            subDataSet = splitDataSet(dataSet,i,value)#例如:收到第一列中是0但又不包括0的数据组
            prob = len(subDataSet)/float(len(dataSet))#例如:第一列中是0但又不包括0的数据组/总数据长度
            newEntropy += prob * calcShannonEnt(subDataSet)#算出这组新数据的熵再乘以这列数据所占的比例+当value=0时算出这组新数据的熵再乘以这列数据所占的比例
        infoGain = baseEntropy - newEntropy#原始数据的熵-新数据的熵
        print("第%d个特征的增益为%.3f" % (i, infoGain))
        if(infoGain > bestInfoGain):    #得出 原始数据的熵-新数据的熵  最小时 的 i(特征值)
            bestInfoGain = infoGain
            bestFeature = i
    return bestFeature

if __name__ == '__main__':
    dataSet, features = createDataSet()
    print("最优特征索引值:" + str(c**hooseBestF**eatureToSplit(dataSet)))

**

运行结果:

**

第0个特征的增益为0.420
第1个特征的增益为0.171
最优特征索引值:0

**

创建树:

**

"""如果数据集已经处理了所有属性,但是类型标签依然不是唯一的,此时我们需要如何定义该叶子节点,在这种情况下,我们
通常会采用多数表决的方法决定叶子节点的分类"""
def majorityCnt(classList):
    classCount = {}
    for vote in classList:
        if vote not in classCount.keys:
            classCount[vote] = 0
        classCount[vote] += 1
        """在classCount这个字典中,operatpr.itemgetter(1)表示按照值的大小进行排序"""
    sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse =True)
    return sortedClassCount[0][0]#取出字典中第一个键值对的键
#创建树的函数代码
def creatTree(dataSet,labels):
    classList = [example[-1] for example in dataSet] #取数据集中每行最后一个数,装在一个新的列表中
    if classList.count(classList[0]) == len(classList): #count(classList[0])表示classList[0]的数目,所有的标签都相等,则返回这个分类标签
        return classList[0]
    if len(dataSet[0]) == 1:#如果数据中只剩标签,则调用多数表决的方法 决定叶子节点的分类
        return majorityCnt(classList)
    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[:]#将剩余的标签取出
        myTree[bestFeatLabel][value] = creatTree(splitDataSet(dataSet,bestFeat,value),subLabels)
    return myTree


if __name__ == '__main__':
    dataSet, features = createDataSet()
    myTree = creatTree(dataSet,features)
    print(myTree)

**

运行结果

**

{'no surfacing ': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值