决策树是在已知各种情况发生概率的基础上,通过构成决策树来求取净现值的期望值大于等于零的概率,评价项目风险,判断其可行性的决策分析方法,是直观运用概率分析的一种图解法。由于这种决策分支画成图形很像一棵树的枝干,故称决策树。在机器学习中,决策树是一个预测模型,他代表的是对象属性与对象值之间的一种映射关系。决策树是一种树形结构,其中每个内部节点表示一个属性上的测试,每个分支代表一个测试输出,每个叶节点代表一种类别。
优点:计算复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可以处理不相关特征数据。
缺点:可能会产生过度匹配问题。
使用数据类型:数值型和标称型。
划分数据集的大原则是:将无序的数据变的更加有序。我们可以使用多种方法划分数据集,但是每种方法都有各自的优缺点。在众多划分方法中评测出最好的划分方式之前,我们必须学习如何计算信息增益。集合信息的度量方式称为香农熵或者简称为熵。
熵定义为信息的期望值,在明晰这个概念之前,我们必须知道信息的定义。如果待分类的事务可能划分在多个分类之中,则符号xi的信息定义为
L() = -p()
其中p(xi)是选择该分类的概率。
为了计算熵,我们需要计算所有类别所有可能包含的信息期望值,通过下面的公式得到:
H = -
其中n是分类的数目。
现集美大学要根据某同学当志愿者(0没有 1少次 2多次),获得奖学金(0没有 1有),参加比赛(0没有 1有),成绩(0中等 1)等信息的情况决定该同学是否具有推优资格(yes或no)。
志愿者 | 奖学金 | 比赛 | 成绩 | 资格 | |
1 | 0 | 0 | 0 | 0 | no |
2 | 0 | 0 | 0 | 1 | no |
3 | 0 | 1 | 0 | 1 | yes |
4 | 0 | 1 | 1 | 0 | yes |
5 | 0 | 0 | 0 | 0 | no |
6 | 1 | 0 | 0 | 0 | no |
7 | 1 | 0 | 0 | 1 | no |
8 | 1 | 1 | 1 | 1 | yes |
9 | 1 | 0 | 1 | 2 | yes |
10 | 1 | 0 | 1 | 2 | yes |
11 | 2 | 0 | 1 | 2 | yes |
12 | 2 | 0 | 1 | 1 | yes |
13 | 2 | 1 | 0 | 1 | yes |
14 | 2 | 1 | 0 | 2 | yes |
15 | 2 | 0 | 0 | 0 | no |
from math import log
import operator
#计算数据集的香农熵
def calcShannonEnt(dataSet):
numEntires = len(dataSet)
labelCounts = {}
#为所有可能分类创建字典
for featVec in dataSet:
currentLabel = featVec[-1]
if currentLabel not in labelCounts.keys():
labelCounts[currentLabel] = 0
labelCounts[currentLabel] += 1
shannonEnt = 0.0
for key in labelCounts:
prob = float(labelCounts[key]) / numEntires
shannonEnt -= prob * log(prob, 2) #以2为底求对数
return shannonEnt
#创建数据集
def createDataSet():
dataSet = [[0, 0, 0, 0, 'no'],
[0, 0, 0, 1, 'no'],
[0, 1, 0, 1, 'yes'],
[0, 1, 1, 0, 'yes'],
[0, 0, 0, 0, 'no'],
[1, 0, 0, 0, 'no'],
[1, 0, 0, 1, 'no'],
[1, 1, 1, 1, 'yes'],
[1, 0, 1, 2, 'yes'],
[1, 0, 1, 2, 'yes'],
[2, 0, 1, 2, 'yes'],
[2, 0, 1, 1, 'yes'],
[2, 1, 0, 1, 'yes'],
[2, 1, 0, 2, 'yes'],
[2, 0, 0, 0, 'no']]
labels = ['志愿者', '奖学金', '比赛', '成绩']
return dataSet, labels
#按照给定特征划分数据集
def splitDataSet(dataSet, axis, value):
retDataSet = [] #创建新的list对象
for featVec in dataSet:
#抽取符合特征的数据
if featVec[axis] == value:
reducedFeatVec = featVec[:axis]
reducedFeatVec.extend(featVec[axis + 1:])
retDataSet.append(reducedFeatVec)
return retDataSet
#选择最好的数据集划分方式
def chooseBestFeatureToSplit(dataSet):
numFeatures = len(dataSet[0]) - 1
baseEntropy = calcShannonEnt(dataSet)
bestInfoGain = 0.0
bestFeature = -1
for i in range(numFeatures):
featList = [example[i] for example 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
print("第%d个特征的增益为%.3f" % (i, infoGain))
#计算最好的信息增益
if (infoGain > bestInfoGain):
bestInfoGain = infoGain
bestFeature = i
return bestFeature
#统计classList中出现此处最多的元素(类标签)
def majorityCnt(classList):
classCount = {}
for vote in classList: # 统计classList中每个元素出现的次数
if vote not in classCount.keys(): classCount[vote] = 0
classCount[vote] += 1
sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True) # 根据字典的值降序排序
return sortedClassCount[0][0] # 返回classList中出现次数最多的元素
决策树的创建:
#创建决策树
def createTree(dataSet, labels, featLabels):
classList = [example[-1] for example in dataSet] # 取分类标签(是否放贷:yes or no)
if classList.count(classList[0]) == len(classList): # 如果类别完全相同则停止继续划分;count()计算一个类别的个数=类别列表里类别数
return classList[0]
if len(dataSet[0]) == 1: # 遍历完所有特征时返回出现次数最多的类标签;没有特征时,用类别投票表决处理
return majorityCnt(classList)
bestFeat = chooseBestFeatureToSplit(dataSet) # 选择最优特征
bestFeatLabel = labels[bestFeat] # 最优特征的标签
featLabels.append(bestFeatLabel)
myTree = {bestFeatLabel: {}} # 根据最优特征的标签生成树
del (labels[bestFeat]) # 删除已经使用特征标签
featValues = [example[bestFeat] for example in dataSet] # 得到训练集中所有最优特征的属性值;
uniqueVals = set(featValues) # 去掉重复的属性值
for value in uniqueVals: # 遍历特征,创建决策树。
# 假设第二列是最优特征,使用该特征作为根节点,进行递归,则原来的dataSet,会变成两个子dataSet,然后对这两个子dataSet分别进行递归创建树,直到满足结束条件
myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), labels, featLabels)
return myTree
if __name__ == '__main__':
dataSet, labels = createDataSet()
featLabels = []
myTree = createTree(dataSet, labels, featLabels)
print(myTree)
使用决策树分类:
#使用决策树分类
def classify(inputTree, featLabels, testVec):
firstStr = next(iter(inputTree)) # 获取决策树结点
secondDict = inputTree[firstStr]
featIndex = featLabels.index(firstStr) # featIndex = 0
for key in secondDict.keys(): # key = 0
if testVec[featIndex] == key:
if type(secondDict[key]).__name__ == 'dict': # 如果是字典类型则要继续递归判断
classLabel = classify(secondDict[key], featLabels, testVec)
else:
classLabel = secondDict[key] # 返回结果值
return classLabel
if __name__ == '__main__':
dataSet, labels = createDataSet()
featLabels = []
myTree = createTree(dataSet, labels, featLabels)
testVec = [0, 0] # 测试数据
result = classify(myTree, featLabels, testVec)
if result == 'yes':
print('推优')
if result == 'no':
print('不推优')