#决策树
1.定义
分类决策树模型是一种描述对实例进行分类的树形结构。决策树由结点和有向边组成。
结点有两种类型:内部节点和叶节点,内部节点表示一个特征或属性,叶节点表示一个类。
分类的时候,从根节点开始,对实例的某一个特征进行测试,根据测试结果,将实例分配到其子结点;
此时,每一个子结点对应着该特征的一个取值。如此递归向下移动,直至达到叶结点,最后将实例分配到叶结点的类中。
#优点:计算复杂度不高,输出结果易于理解,对中间值缺失不敏感,可以处理不相关特征数据
#缺点:可能会产生过度匹配问题
#适用数据类型:数值型和标称型
'''
createBranch()函数伪代码
检测数据集中的每个子项是否属于同一类分类:
If so return 类标签:
Else
寻找划分数据集的最好特征
划分数据集
创建分支节点
for 每个划分的子类
调用函数createBranch并增加返回结果到分支节点中
return 分支节点
决策树一般流程:
收集-准备-分析数据-训练-测试-使用算法
信息增益:在划分数据集之前之后信息发生的变化
获得信息增益最高的特征就是最好的选择
香农熵(熵):集合信息的度量方式
熵定义为信息的期望值
'''
1 #计算给定数据集的香农熵
2 from math import log
3 from matplotlib.font_manager import FontProperties
4 import matplotlib.pyplot as plt
5 import operator
6 from importlib import reload
7 def calcShannonEnt(dataSet):
8 numEntries = len(dataSet)
9 labelCounts = {} #创建一个数据字典,它的键值是最后一列的数值
10 for featVec in dataSet:
11 currentLabel = featVec[-1]
12 if currentLabel not in labelCounts.keys():
13 labelCounts[currentLabel] = 0
14 labelCounts[currentLabel] += 1
15 shannonEnt = 0.0
16 for key in labelCounts: #使用所有类标签的发生频率计算类别出现的概率
17 prob = float(labelCounts[key])/numEntries #利用这个概率计算香农熵
18 shannonEnt -= prob * log(prob,2)
19 return shannonEnt
20 def createDataSet():
21 dataSet = [[1,1,'yes'],[1,1,'yes'],[1,0,'no'],[0,1,'no'],[0,1,'no']]
22 labels = ['no surfacing','flippers']
23 return dataSet, labels
24
25 #划分数据集
26 def splitDataSet(dataSet, axis, value): #待划分数据集,划分数据集的特征,特征的返回值
27 retDataSet = [] #创建新的列表对象
28 for featVec in dataSet:
29 if featVec[axis] == value: #将符合特征的数据抽取出来
30 reducedFeatVec = featVec[:axis]
31 reducedFeatVec.extend(featVec[axis+1:])
32 retDataSet.append(reducedFeatVec)
33 return retDataSet
34
35 def chooseBestFeatureToSplitByID3(dataSet):
36 '''
37 选择最好的数据集划分方式
38 param dataSet:数据集
39 return: 划分结果
40 '''
41 numFeatures = len(dataSet[0]) - 1 # 最后一列yes分类标签,不属于特征向量
42 baseEntropy = calcShannonEnt(dataSet)
43 bestInfoGain = 0.0
44 bestFeature = -1
45 for i in range(numFeatures): # 遍历所有特征
46 featList = [example[i] for example in dataSet]
47 uniqueVals = set(featList)
48 newEntropy = 0.0
49 for value in uniqueVals: #创建唯一的分类标签列表
50 subDataSet = splitDataSet(dataSet, i, value)
51 prob = len(subDataSet)/float(len(dataSet))
52 newEntropy += prob * calcShannonEnt(subDataSet)
53 infoGain = baseEntropy - newEntropy # 计算信息增益
54 if (infoGain > bestInfoGain): # 选择最大的信息增益
55 bestInfoGain = infoGain
56 bestFeature = i
57 return bestFeature # 返回最优特征对应的维度
58
59 #递归构建决策树
60 def majorityCnt(classList):
61 #采用多数表决的方法决定叶结点的分类,param: 所有的类标签列表,return: 出现次数最多的类
62 classCount = {}
63 for vote in classList: # 统计所有类标签的频数
64 if vote not in classCount.keys():
65 classCount[vote] = 0
66 classCount[vote] += 1 #利用operation操作键值排序字典,并返回出现次数最多的分类名称
67 sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True) # 排序
68 return sortedClassCount[0][0]
69
70 #创建数的函数代码
71 def createTree(dataSet,labels):
72 #param: dataSet:训练数据集
73 #return: labels:所有的类标签
74 classList = [example[-1] for example in dataSet]
75 if classList.count(classList[0]) == len(classList):
76 return classList[0] # 第一个递归结束条件:所有的类标签完全相同
77 if len(dataSet[0]) == 1:
78 return majorityCnt(classList) # 第二个递归结束条件:用完了所有特征
79 bestFeat = chooseBestFeatureToSplitByID3(dataSet) # 最优划分特征
80 bestFeatLabel = labels[bestFeat]
81 myTree = {bestFeatLabel:{}} # 使用字典类型储存树的信息
82 del(labels[bestFeat])
83 featValues = [example[bestFeat] for example in dataSet]
84 uniqueVals = set(featValues)
85 for value in uniqueVals:
86 subLabels = labels[:] # 复制所有类标签,保证每次递归调用时不改变原始列表的内容
87 myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels)
88 return myTree