决策树算法(四)——选取最佳特征划分数据集

写在前面的话

咦,好神奇,我今天发现我莫名其妙的成了小三。原来我被卖了还不知道,还帮人家宣传博客和微信公众账号。我放弃了自己规划好的人生,然后决定跟另一个在一起,然后发现原来他有女朋友。我今天有点头大哎。

哈哈哈哈哈。原来被劈腿也会被我遇到呢。

可怜了我这个系列的博客,写的这么好却没有人知道欣赏。就像我这么好,哈哈哈,我好不要脸,却。。。。

如果您有任何地方看不懂的,那一定是我写的不好,请您告诉我,我会争取写的更加简单易懂!

如果您有任何地方看着不爽,请您尽情的喷,使劲的喷,不要命的喷,您的槽点就是帮助我要进步的地方!

1.划分数据集

1.1 基本概念

在度量数据集的无序程度的时候,分类算法除了需要测量信息熵,还需要划分数据集,度量花费数据集的熵,以便判断当前是否正确的划分了数据集。

我们将对每个特征数据集划分的结果计算一次信息熵,然后判断按照那个特征划分数据集是最好的划分方式。

也就是说,我们依次选取我们数据集当中的所有特征作为我们划定的特征,然后计算选取该特征时的信息增益,当信息增益最大时我们就选取对应信息增益最大的特征作为我们分类的最佳特征。

下面是我们的数据集:

我们用python语言表示出这个数据集

dataSet= [[1, 1, ‘yes’], [1, 1, ‘yes’], [1, 0, ‘no’], [0, 1, ‘no’], [0, 1, ‘no’]]

在这个数据集当中有两个特征,就是每个样本的第一列和第二列,最后一列是它们所属的分类。

我们划分数据集是为了计算根据那个特征我们可以得到最大的信息增益,那么根据这个特征来划分数据就是最好的分类方法。

因此我们需要遍历每一个特征,然后计算按照这种划分方式得出的信息增益。信息增益是指数据集在划分数据前后信息的变化量。


1.2 具体操作

划分数据集的方式我们首先选取第一个特征的第一个可能取值来筛选信息。然后再选取第一个特征的第二个可能的取值来划分我们的信息。之后我们再选取第二个特征的第一个可能的取值来划分数据集,以此类推。

e.g:

[[1, 1, ‘yes’], [1, 1, ‘yes’], [1, 0, ‘no’], [0, 1, ‘no’], [0, 1, ‘no’]]

这个是我们的数据集。

如果我们选取第一个特征值也就是需不需要浮到水面上才能生存来划分我们的数据,这里生物有两种可能,1就是需要,0就是不需要。那么第一个特征的取值就是两种。

如果我们按照第一个特征的第一个可能的取值来划分数据也就是当所有的样本的第一列取1的时候满足的样本,那就是如下三个:

[1, 1, ‘yes’], [1, 1, ‘yes’], [1, 0, ‘no’]

可以理解为这个特征为一条分界线,我们选取完这个特征之后这个特征就要从我们数据集中剔除,因为要把他理解为分界线。那么划分好的数据就是:

[[1, ‘yes’], [1, ‘yes’], [0, ‘no’]]

如果我们以第一个特征的第二个取值来划分数据集,那么得到的数据子集就是下面这个样子:

[[1, ‘no’], [1, ‘no’]]



因此我们可以很容易的来构建出我们的代码:

下面我们来分析一下这段代码,

# 代码功能:划分数据集
def splitDataSet(dataSet,axis,value): #传入三个参数第一个参数是我们的数据集,是一个链表形式的数据集;第二个参数是我们的要依据某个特征来划分数据集
    retDataSet = [] #由于参数的链表dataSet我们拿到的是它的地址,也就是引用,直接在链表上操作会改变它的数值,所以我们新建一格链表来做操作

    for featVec in dataSet:
        if featVec[axis] == value: #如果某个特征和我们指定的特征值相等
        #除去这个特征然后创建一个子特征
            reduceFeatVec = featVec[:axis]
            reduceFeatVec.extend(featVec[axis+1:])
            #将满足条件的样本并且经过切割后的样本都加入到我们新建立的样本中
            retDataSet.append(reduceFeatVec)

    return retDataSet

总的来说,这段代码的功能就是按照某个特征的取值来划分数据集。

为方便您测试实验我们在贴出这段代码:


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

在这里我们可以注意到一个关于链表的操作:

那就是extend 和append

它们的用法和区别如下所示:



下面我们再来测试一下我们的数据:

先给出实验的完整代码

#!/usr/bin/env python
# coding=utf-8
# author: chicho
# running: python trees.py
# filename : trees.py
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 calcShannonEnt(dataSet):
    countDataSet = len(dataSet)
    labelCounts={}
    for featVec in dataSet:
        currentLabel=featVec[-1]
        if currentLabel not in labelCounts.keys():
            labelCounts[currentLabel] = 0
        labelCounts[currentLabel] += 1

    print labelCounts

    shannonEnt = 0.0

    for key in labelCounts:
        prob = float(labelCounts[key])/countDataSet
        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

我们输入下面代码测试一下,你可以在linux系统的终端中输入python或者是ipython 来进行测试:

In [1]: import trees

In [2]: reload(trees)
Out[2]: <module ‘trees‘ from ‘trees.pyc‘>

In [3]: myDat,labels=trees.createDataSet()

In [4]: myDat
Out[4]: [[1, 1, ‘yes‘], [1, 1, ‘yes‘], [1, 0, ‘no‘], [0, 1, ‘no‘], [0, 1, ‘no‘]]

In [5]: trees.splitDataSet(myDat,0,1)
Out[5]: [[1, ‘yes‘], [1, ‘yes‘], [0, ‘no‘]]

In [6]: trees.splitDataSet(myDat,0,0)
Out[6]: [[1, ‘no‘], [1, ‘no‘]]


知道怎么划分数据集之后,我们接下来的工作就是遍历整个样本集合的特征值,然后循环计算香农熵,找到最好的特征划分方式。


2.计算信息增益

我们主要是要找到划分数据前后的最大信息增益,然后找到根据那个特征来做划分,分类出来的效果更好。

下面给出代码是怎么实现的

我们来分析一下代码:

注意: 在使用这段代码的时候我们对处理的数据是有要求的。

1.数据集必须是链表的表示出来的

2.数据集的每一个样本也是链表形式

3.数据集的每一个样本都必须是前面的所有列都是样本的特征值的取值范围,所有样本的最后一列是样本的类别。

4.每个样本的列数必须相同

首先我们的样本集合是:

dataSet = [[1,1,’yes’],

[1,1,’yes’],

[1,0,’no’],

[0,1,’no’],

[0,1,’no’]] # 我们定义了一个list来表示我们的数据集,这里的数据对应的是上表中的数据



我们的目的已经很明确了,就是依次遍历每一个特征,在这里我们的特征只有两个,就是需不需要浮出水面,有没有脚蹼。然后计算出根据每一个特征划分产生的数据集的熵,和初始的数据集的熵比较,我们找出和初始数据集差距最大的。那么这个特征就是我们划分时最合适的分类特征。

def chooseBestFeatureToSplit(dataSet):
    numFeatures = len(dataSet[0])-1 # 获取我们样本集中的某一个样本的特征数(因为每一个样本的特征数是相同的,相当于这个代码就是我们可以作为分类依据的所有特征个数)我们的样本最后一列是样本所属的类别,所以要减去类别信息,在我们的例子中特征数就是2
    baseEntropy = calcShannonEnt(dataSet) #计算样本的初始香农熵
    bestInfoGain =0.0 #初始化最大信息增益
    bestFeature = -1 #和最佳划分特征

    for i in range(numFeatures): # range(2)那么i的取值就是0,1。 在这里i表示的我们的第几个特征
        featList = [sample[i] for sample in dataSet]
        # 我们首先遍历整个数据集,首先得到第一个特征值可能的取值,然后把它赋值给一个链表,我们第一个特征值取值是[1,1,1,0,0],其实只有【1,0】两个取值
        uniqueVals = set(featList)#我们使用集合这个数据类型删除多余重复的原始使得其中只有唯一的值。
        #执行的结果如下所示:
        ```
        In [8]: featList=[1,1,1,0,0]

        In [9]: uniqueVals=set(featList)

        In [10]: uniqueVals
        Out[10]: {0, 1}

        ```
        newEntropy = 0.0
        for value in uniqueVals: #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 chooseBestFeatureToSplit(dataSet):
    numFeatures = len(dataSet[0])-1
    baseEntropy = calcShannonEnt(dataSet)
    bestInfoGain =0.0
    bestFeature = -1

    for i in range(numFeatures):
        featList = [sample[i] for sample in dataSet]
        uniqueVals = set(featList)
        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


难点

下面我们来重点解释一下刚才我们没有说的那句代码:

newEntropy += prob * calcShannonEnt(subDataSet)

为什么根据这个代码就可以计算出划分子集的熵了呢?

首先我们还是要回顾一下计算数学期望的方法,我们必须明确熵是信息的数学期望。

对于随机变量要计算它的数学期望我们是这样来计算的:

X X1 X2
p p1 p2

E(x)=P1*X1 + P2 *X2

我么都以第一特征值来说明问题

同理,我们可以先计算出按照第一个特征值的第一个可能取值划分满足的子集的概率,和第一个特征值的第二个可能取值划分满足子集的概率。之后分别乘以它们子集计算出的香农熵。

香农熵 H1 H2
特征值划分出子集概率 p1 p2

E(H)=H1×P1+H2×P2

e.g. 第一个特征为例

这是我们的数据集

我们按照第一个特征的第一个取值划分得到的数据集是:

得到了两个数据集,那么占总的数据集就是2/5

我们按照第一个特征的第二个取值划分得到的数据集是:

得到了三个数据集,那么占总的数据集就是3/5

我们分别来计算一下它们的香农熵

××××××

我们观察一下数据,也充分的验证了分类越多,香农熵会越大,当我们的分类只有一类是香农熵是0

××××××

我们采用列表法来计算一下熵:

某个特征的不同取值对应的香农熵 0.0 0.9182958340544896
特征值划分出子集概率 0.4 0.6

根据期望的定义,我们在第一章讲过的公式,那么就可以计算出这个特征的信息熵



我们来测试一下结果:


In [1]: import trees

In [2]: reload(trees)
Out[2]: <module ‘trees‘ from ‘trees.pyc‘>

In [3]: myDat,labels=trees.createDataSet()

In [4]: myDat
Out[4]: [[1, 1, ‘yes‘], [1, 1, ‘yes‘], [1, 0, ‘no‘], [0, 1, ‘no‘], [0, 1, ‘no‘]]

In [5]: trees.chooseBestFeatureToSplit(myDat)
#总的数据分类
{‘yes‘: 2, ‘no‘: 3}
#按照第一个特征的第一个取值来划分
{‘no‘: 2}
#按照第一个特征的第二个取值来划分
{‘yes‘: 2, ‘no‘: 1}
##按照第二个特征的第一个取值来划分
{‘no‘: 1}
#按照第二个特征的第二个取值来划分
{‘yes‘: 2, ‘no‘: 2}
Out[5]: 0

我们可以看出计算的结果是根据第一个特征划分比较好。

这个其实也很明显了,我们可以观察一下我们的数据按照第一个特征来划分,当特征为1时生物分组有两个属于鱼类,一个属于非鱼类,另一个分组全部属于非鱼类。

如果按照第二个特征分类,一组中两个属于鱼类,两个属于非鱼来,另一组中只有一个是非鱼类。也就是按照第二特征来划分,错误率比较大。



完整的代码:

#!/usr/bin/env python
# coding=utf-8
# author: chicho
# running: python trees.py
# filename : trees.py
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 calcShannonEnt(dataSet):
    countDataSet = len(dataSet)
    labelCounts={}
    for featVec in dataSet:
        currentLabel=featVec[-1]
        if currentLabel not in labelCounts.keys():
            labelCounts[currentLabel] = 0
        labelCounts[currentLabel] += 1

    print labelCounts

    shannonEnt = 0.0

    for key in labelCounts:
        prob = float(labelCounts[key])/countDataSet
        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 = [sample[i] for sample in dataSet]
        uniqueVals = set(featList)
        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

写在后面的话

要么就不做,要做就做最好

时间: 2024-10-13 06:43:17

决策树算法(四)——选取最佳特征划分数据集的相关文章

决策树算法(一)——一些重要的数学概念

写在前面的话 趁着现在我还是高中数理化老师,偶尔兼职英语老师的时候赶紧抓紧时间写点有关计算机科学技术的东西.一来是表示我对计算机的热爱,二来,当然是最重要的咯,满足一下我强大的虚荣心.哈哈哈哈!想想高中数学物理化学老师在折腾计算机,是不是有种瞬间吊炸天的感觉. 这个系列我写了一个月了,之后会陆陆续续的放出来的.希望对大家有一点点帮助.如果您没有看懂我在写啥,那一定是我错了,讲的不够清楚.世界上没有什么知识点是难的,只是看你知识储备和理解力达到了相应的水平没有.至少我是这么认为的. 当然,您要是觉

《机器学习实战》基于信息论的三种决策树算法(ID3,C4.5,CART)

============================================================================================ <机器学习实战>系列博客是博主阅读<机器学习实战>这本书的笔记,包含对其中算法的理解和算法的Python代码实现 另外博主这里有机器学习实战这本书的所有算法源代码和算法所用到的源文件,有需要的留言 如需转载请注明出处,谢谢 ======================================

Python机器学习(三)--决策树算法

一.决策树原理 决策树是用样本的属性作为结点,用属性的取值作为分支的树结构. 决策树的根结点是所有样本中信息量最大的属性.树的中间结点是该结点为根的子树所包含的样本子集中信息量最大的属性.决策树的叶结点是样本的类别值.决策树是一种知识表示形式,它是对所有样本数据的高度概括决策树能准确地识别所有样本的类别,也能有效地识别新样本的类别. 决策树算法ID3的基本思想: 首先找出最有判别力的属性,把样例分成多个子集,每个子集又选择最有判别力的属性进行划分,一直进行到所有子集仅包含同一类型的数据为止.最后

吴裕雄--天生自然python机器学习:决策树算法

我们经常使用决策树处理分类问题’近来的调查表明决策树也是最经常使用的数据挖掘算法. 它之所以如此流行,一个很重要的原因就是使用者基本上不用了解机器学习算法,也不用深究它 是如何工作的. K-近邻算法可以完成很多分类任务,但是它最大的缺点就是无法给出数据的内 在含义,决策树的主要优势就在于数据形式非常容易理解. 决策树很多任务都 是为了数据中所蕴含的知识信息,因此决策树可以使用不熟悉的数据集合,并从中提取出一系列 规则,机器学习算法最终将使用这些机器从数据集中创造的规则.专家系统中经常使用决策树,

机器学习实践之决策树算法学习

关于本文说明,本人原博客地址位于http://blog.csdn.net/qq_37608890,本文来自笔者于2017年12月06日 18:06:30所撰写内容(http://blog.csdn.net/qq_37608890/article/details/78731169).   本文根据最近学习机器学习书籍 网络文章的情况,特将一些学习思路做了归纳整理,详情如下.如有不当之处,请各位大拿多多指点,在此谢过. 一.决策树(decision tree)概述 1.决策树概念 决策树(decis

4-Spark高级数据分析-第四章 用决策树算法预测森林植被

预测是非常困难的,更别提预测未来. 4.1 回归简介 随着现代机器学习和数据科学的出现,我们依旧把从“某些值”预测“另外某个值”的思想称为回归.回归是预测一个数值型数量,比如大小.收入和温度,而分类则指预测标号或类别,比如判断邮件是否为“垃圾邮件”,拼图游戏的图案是否为“猫”. 将回归和分类联系在一起是因为两者都可以通过一个(或更多)值预测另一个(或多个)值.为了能够做出预测,两者都需要从一组输入和输出中学习预测规则.在学习的过程中,需要告诉它们问题及问题的答案.因此,它们都属于所谓的监督学习.

决策树算法原理及实现

(一)认识决策树 1.决策树分类原理 决策树是通过一系列规则对数据进行分类的过程.它提供一种在什么条件下会得到什么值的类似规则的方法.决策树分为分类树和回归树两种,分类树对离散变量做决策树,回归树对连续变量做决策树. 近来的调查表明决策树也是最经常使用的数据挖掘算法,它的概念非常简单.决策树算法之所以如此流行,一个很重要的原因就是使用者基本上不用了解机器学习算法,也不用深究它是如何工作的.直观看上去,决策树分类器就像判断模块和终止块组成的流程图,终止块表示分类结果(也就是树的叶子).判断模块表示

决策树算法

利用ID3算法来判断某天是否适合打网球. (1)类别属性信息熵的计算由于未分区前,训练数据集中共有14个实例, 其中有9个实例属于yes类(适合打网球的),5个实例属于no类(不适合打网球), 因此分区前类别属性的熵为: (2)非类别属性信息熵的计算,若先选择Outlook属性. (3)Outlook属性的信息增益为: (4)同理计算出其他3个非类别属性的信息增益,取最大的那个属性作为分裂节点,此例中最大的是Outlook,进而得到如下图所示: (5)上图中,针对sunny中的子训练数据集分支,

决策树算法原理

转载于:http://www.cnblogs.com/pinard/p/6050306.html (楼主总结的很好,就拿来主义了,不顾以后还是多像楼主学习) 决策树算法在机器学习中算是很经典的一个算法系列了.它既可以作为分类算法,也可以作为回归算法,同时也特别适合集成学习比如随机森林.本文就对决策树算法原理做一个总结,上篇对ID3, C4.5的算法思想做了总结,下篇重点对CART算法做一个详细的介绍.选择CART做重点介绍的原因是scikit-learn使用了优化版的CART算法作为其决策树算法