决策树(一)
#决策树
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