机器学习实战-决策树

决策树:

 

1. 计算数据集的香农熵

 1 #计算给定数据集的香农熵
 2 def calcShannonEnt (dataset):
 3     # 求数据list的长度,表示计算参与训练的数据量
 4     numEnties = len(dataset)
 5     # 计算分类标签label出现的次数
 6     labelCounts={}
 7     for featVec in dataset:
 8         # 将当前实例的标签存储,即每一行数据的最后一个数据代表的是标签
 9         currentLabel= featVec[-1]
10         # 为所有可能的分类创建字典,如果当前的键值不存在,则扩展字典并将当前键值加入自覅,每个键值都记录了
11         #当前类别出现的次数
12         if currentLabel not in labelCounts.keys():
13          labelCounts[currentLabel]=0
14         labelCounts[currentLabel]+=1
15     # 对于label标签的占比,求出label标签label标签的香农熵
16     shannonEnt=0.0
17     #计算熵,求p(),累对数
18     for key in labelCounts:
19         # 使用所有类标签的发生频率计算类别出现的概率
20         prob=float(labelCounts[key])/numEnties
21         # log base 2 计算香农熵,以2为底求对数
22         shannonEnt-=prob * log(prob,2)
23     return shannonEnt

2. 创建数据集

1 # 创建数据集
2 def createDataSet():
3     dataSet = [[1,1,'yes'],
4                [1,1,'yes'],
5                [1,0,'no'],
6                [0,1,'no'],
7                [0,1,'no']]
8     labels = ['no surfacing','flippers']
9     return dataSet,labels

 

3.  划分数据集:度量花费数据集的熵,以便判断当前是否正确地划分了数据集。我们将对每个特征划分数据集的结果计算一次信息熵,然后判断按照哪个特征划分数据集是最好的划分方式。

 1 # 按照给定特征划分数据集
 2 def splitDataSet(dataSet, index, value):
 3     """
 4     Desc:
 5         划分数据集
 6         splitDataSet(通过遍历dataSet数据集,求出index对应的colnum列的值为value的行)
 7         就是依据index列进行分类,如果index列的数据等于 value的时候,就要将 index 划分到我们创建的新的数据集中
 8     Args:
 9         dataSet  -- 数据集                 待划分的数据集
10         index -- 表示每一行的index列        划分数据集的特征
11         value -- 表示index列对应的value值   需要返回的特征的值。
12     Returns:
13         index 列为 value 的数据集【该数据集需要排除index列】
14     """
15     retDataSet = []
16     # 目的是不要指定index列数据的新数据集
17     for featVec in dataSet:
18         if featVec[index] == value:
19             # [:index]表示前index行,即若 index 为2,就是取 featVec 的前 index 行
20             reduceFeatVec =featVec[:index]
21             '''
22             请百度查询一下: extend和append的区别
23             list.append(object) 向列表中添加一个对象object
24             list.extend(sequence) 把一个序列seq的内容添加到列表中
25             1、使用append的时候,是将new_media看作一个对象,整体打包添加到music_media对象中。
26             2、使用extend的时候,是将new_media看作一个序列,将这个序列和music_media序列合并,并放在其后面。
27             result = []
28             result.extend([1,2,3])
29             print(result)
30             result.append([4,5,6])
31             print(result)
32             result.extend([7,8,9])
33             print(result)
34             结果:
35             [1, 2, 3]
36             [1, 2, 3, [4, 5, 6]]
37             [1, 2, 3, [4, 5, 6], 7, 8, 9]
38             '''
39             reduceFeatVec.extend(featVec[index+1:])
40             # [index+1:]表示从跳过 index 的 index+1行,取接下来的数据
41             # 收集结果值 index列为value的行【该行需要排除index列】
42             retDataSet.append(reduceFeatVec)
43     return retDataSet

 

3. 选择最好的数据集划分方式。实现选取特征,划分数据集,计算得到最好的划分数据集特征。

 1 def chooseBsetFeatureToSplit (dataSet):
 2     """
 3     选择切分数据集的最佳特征
 4     :param dataSet: 需要切分的数据集
 5     :return: bestFeature:切分数据集的最优特征列
 6     """
 7     # 得到特征个数,求第一行有多少列的feature,我们知道数据的最后一列是label,所以不需要。
 8     numFeatures = len(dataSet[0])-1
 9     # 计算数据的label的信息熵
10     baseEntropy = calcShannonEnt(dataSet)
11     # 定义最优的信息增益值,以及最优的特征标号变量
12     bestInfoGain =0.0; bestFeature = -1
13     # 迭代所有的特征
14     for i in range (numFeatures):
15         #获取每一个实例的第i+1个feature,组成一个list集合。
16         featList=[example[i] for example in dataSet]
17         # 获取去重后的集合,使用set对list数据进行去重
18         uniqueVals=set(featList)
19         # 创建一个临时的信息熵
20         newEntropy=0.0
21         # 遍历某一列的value集合,计算该列的信息熵
22         # 遍历当前特征中的所有唯一属性值,对每个唯一属性值划分一次数据集,计算数据集的新熵值,并对所有唯一特征值得到得到熵求和
23         for value in uniqueVals:
24             subDataSet=splitDataSet(dataSet,i,value)
25             prob=len(subDataSet)/float(len(dataSet))
26             newEntropy+=prob * calcShannonEnt(subDataSet)
27         # gain是信息增益:划分数据前后的信息变化,获取信息熵最大的值
28         # 信息增益是熵的减少或者是数据无序度的减少,最后,比较所有特征中的信息增益,返回最好特征划分的索引值。
29         infoGain = baseEntropy-newEntropy
30         print('infoGain=',infoGain,'bestFeature=',i,baseEntropy,newEntropy)
31         if(infoGain>bestInfoGain):
32             bestInfoGain=infoGain
33             bestFeature=i
34         return bestFeature

 

 

4. 由于特征值可能多于2个,因此可能存在大于两个分支的数据集划分,第一次划分之后,数据将被向下传递到树分支的下一个支点,在这个节点上,我们可以再次划分数据。如果数据集已经处理了所有属性,但是类标签依然不是唯一的,此时我们需要决定定义该叶子节点,在这种情况下,我们通常会采用多数表决得到方法决定该叶子节点的分类。

此时我们编写该功能,并加上import operator.

 1 def majorityCnt(classList):
 2     """
 3     选择出现次数最多的一个结果
 4     :param classList: label列的集合
 5     :return: bestFeature最优的特征列
 6     """
 7     classCount={}
 8     for vote in classList:
 9         if vote not in classCount.keys():
10             classCount[vote]=0
11         classCount[vote]+=1
12     # 倒序排列classCount得到一个字典集合,然后取出第一个就是结果,即出现次数最多的结果
13     sortedClassCount=sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
14     return sortedClassCount[0][0]

 

 

5. 创建决策树

 1 def createTree(dataSet,labels):
 2     """
 3     创建决策树
 4     :param dataSet: 要创建决策树的训练数据集
 5     :param labels: 训练数据集中特征对应的含义的labels,不是目标变量
 6     :return:myTree:创建完成的决策树
 7     """
 8     # 获取所有类别
 9     classList=[example[-1] for example in dataSet]
10     # 如果数据集的最后一类的第一个值出现的次数等于整个集合的数量,也就是说只有一个类别,就只直接返回结果就行
11     if classList.count(classList[0])== len(classList):
12         return classList[0]
13     # 如果数据集只有一列,那么最初出现label次数最多的一类,作为结果
14     if len(dataSet[0])==1:
15         return majorityCnt(classList)
16 
17     # 选择最优的列,得到最优列对应的label含义
18     bestFeat= chooseBsetFeatureToSplit(dataSet)
19     # 获取label的名称
20     bestFeatLabel =labels[bestFeat]
21     #初始化myTree
22     myTree={bestFeatLabel:{}}
23     # lables列表是可变对象,在python函数中作为参数时传址引用,能够被全局修改
24     del(labels[bestFeat])
25     #取出最优列,将后他的branch做分类
26     featValues=[example[bestFeat] for example in dataSet]
27     uniqueVals=set(featValues)
28     for value in uniqueVals:
29         # 求出剩余的标签label,复制类标签,存在在新列表上
30         subLabels=labels[:]
31         # 遍历当前选择特征包含的所有属性值,在每个数据集划分上递归调用函数createTree()
32         myTree[bestFeatLabel][value]=createTree(splitDataSet(dataSet,bestFeat,value),subLabels)
33     return myTree

 

 6. 使用决策树进行分类。

 1 def classify(inputTree,featLabels,testVec):
 2     """
 3     对新数据进行分类
 4     :param inputTree: 已经训练好的决策树模型
 5     :param featLabels: Feature标签对应的名称,不是目标变量
 6     :param testVec: 测试输入的数据
 7     :return: 分类的结果值,需要映射label知道名称
 8     """
 9     #获取tree的根节点对于key值
10     firstStr=list(inputTree.keys())[0]
11     #通过key得到根节点对应的value
12     secondDict=inputTree[firstStr]
13     # 判断根节点名称获取根节点在label中的先后顺序,这样就知道输入的testVec怎么开始对照树来做分类
14     featIndex=featLabels.index(firstStr)
15     #测试数据,找到根节点对应的label位置,也就知道从输入的数据的第几位来开始分类
16     key=testVec[featIndex]
17     valueOfFeat=secondDict[key]
18     #判断分枝是否结束,判断valueOfFeat是否是dict类型
19     if isinstance(valueOfFeat,dict):
20         classLabel=classify(valueOfFeat,featLabels,testVec)
21     else:
22         classLabel=valueOfFeat
23     return classLabel

 

7. 测试数据

 1 def fishTest():
 2     """
 3     Desc:
 4         对动物是否是鱼类分类的测试函数,并将结果使用 matplotlib 画出来
 5     Args:
 6         None
 7     Returns:
 8         None
 9     """
10     # 1.创建数据和结果标签
11     myDat, labels = createDataSet()
12     # print(myDat, labels)
13 
14     # 计算label分类标签的香农熵
15     # calcShannonEnt(myDat)
16 
17     # # 求第0列 为 1/0的列的数据集【排除第0列】
18     # print('1---', splitDataSet(myDat, 0, 1))
19     # print('0---', splitDataSet(myDat, 0, 0))
20 
21     # # 计算最好的信息增益的列
22     # print(chooseBestFeatureToSplit(myDat))
23 
24     import copy
25     myTree = createTree(myDat, copy.deepcopy(labels))
26     print(myTree)
27     # [1, 1]表示要取的分支上的节点位置,对应的结果值
28     print(classify(myTree, labels, [1, 1]))

 

 

测试结果为yes

相应的github地址:https://github.com/CynthiaWendy/Machine-Learning-in-Action-DecisionTree

 

posted @ 2019-07-18 17:22  轩窗尘清  阅读(199)  评论(0编辑  收藏  举报