我是靠谱客的博主 文静芝麻,最近开发中收集的这篇文章主要介绍自学机器学习实战(peter harrington)决策树及其可视化的python代码练习数据集和特征标签计算集合的信息(熵)抽样:抽取某个特征取定值的数据集根据信息增益选出最好的特征多数投票原则给定数据和特征 生成决策树决策树可视化画箭头指向文本用于注释生成决策树图可视化判断结果 放在直线上计算叶节点数计算深度或者高度储存生成的树利用树进行分类树的储存和读取,觉得挺不错的,现在分享给大家,希望可以做个参考。
概述
机器学习实战之决策树
- 练习数据集和特征标签
- 计算集合的信息(熵)
- 抽样:抽取某个特征取定值的数据集
- 根据信息增益选出最好的特征
- 多数投票原则
- 给定数据和特征 生成决策树
- 决策树可视化
- 画箭头指向文本用于注释
- 生成决策树图
- 可视化判断结果 放在直线上
- 计算叶节点数
- 计算深度或者高度
- 储存生成的树
- 利用树进行分类
- 树的储存和读取
from math import log
import matplotlib.pyplot as plt
import pickle
练习数据集和特征标签
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):
# 对传入数据集计算香农熵
m = len(dataSet)
# 这里的dataset传入或者利用read读取
labelCounts = {}
# 创建字典计数,同样的类标签出现了几次即是不是某一类
# 标签从feavec中取倒数第一个即数据阵的最后一列
for featVec in dataSet:
currentLabel = featVec[-1]
if currentLabel not in labelCounts.keys():
labelCounts[currentLabel] = 0
# 如果字典中没有该键则创建一个新键
# 如果在字典中
labelCounts[currentLabel] += 1
# 将两类标签出现的次数统计出来
ShannonEnt = 0
for key in labelCounts:
# 计算每种分类的频率
prob = float(labelCounts[key])/m
ShannonEnt -= prob * log(prob, 2)
# 熵是-的期望所以-=
# 计算集合的熵 表示信息量
# 如果被分成n类,如果有一类被选择的概率大,那么
# 信息小,比较纯,不用再细分
return ShannonEnt
抽样:抽取某个特征取定值的数据集
def splitDataSet(dataSet, axis, value):
# 轴特征一样的属性值,去掉创建新的数据集
# 下一个分支的叶子
# axis是数据某个属性 value是对应的值
retDataSet = []
for featVec in dataSet:
# 每一行数据都是一个特征向量
if featVec[axis] == value:
# 如果这一行数据特征值等于给定的特征值
# 那么这一行中axis这一数据
# 这样就按照这个特征把数据分成了两类
# 一类标记为0,一类标记为1
# 这里如果三类怎么办?
# 看到创建树的函数,做法就是遍历最好分类标签
# 所有可能取值,这样就得到所有类的剥离后的数据集
reducedfeatVec = featVec[:axis]
# 中间没有逗号
reducedfeatVec.extend(featVec[axis + 1:])
retDataSet.append(reducedfeatVec)
# 返回的是裁剪掉的向量 特征维度减少了1
# 样本维度也变少了
return retDataSet
根据信息增益选出最好的特征
def chooseBestFeatureToSplit(dataSet):
# 选择最好的特征来创建子集
# 根据熵是否足够小,划分数据集
# 即数据集纯度要足够,选择决策树的分点
num_feature = len(dataSet[0]) - 1
# 计算特征种类数
baseEnt = calcShannonEnt(dataSet)
# 计算总数据集的熵
baseinfoGain = 0
# 初始化信息增益
bestfeature = -1
# 初始化特征
for i in range(num_feature):
# 遍历所有特征,不取最后一列数据
featlist = [example[i] for example in dataSet]
# 一次循环所有i属性的值都储存在列表中
# 二次循环储存j属性的所有值
newEnt = 0
# 初始化熵
for value in featlist:
# 相当于从特征向量中取值
subDataSet = splitDataSet(dataSet, i, value)
# 对第i个属性进行甄别,获得剥离特征后的数据集
# 这里的新数据集第i个属性值是相同的
# 所以数据个数会减少
prob = float(len(subDataSet) / len(dataSet))
# 计算子集被抽出来的概率
newEnt += prob * calcShannonEnt(subDataSet)
# 相当于概率论的乘法公式,第一步取子集
# 第二步在子集上计算熵
# 所有可能的值累加起来计算期望:熵
# 内循环计算按每个特征i分类后子集的信息量
infoGain = baseEnt - newEnt
# 计算熵的变化,即信息增益
# 注意越纯即叶节点,信息越少,即差异小
if infoGain > baseinfoGain:
baseinfoGain = infoGain
# 如果信息增益一直增加
# 那么改变base,直到信息增益开始变小
# 这样就选出了最好的分类特征
bestfeature = i
return bestfeature
多数投票原则
def majorityCnt(classlist):
# 多数投票原则,返回投票次数即出现最多的分类即为训练的分类
# 不止是两个分类,只是再不可划分集合的时候直接分类用到。
classCount = {}
# 分类结果计数字典
for vote in classlist:
# 遍历分类列表(传入)
if vote not in classCount.keys():
# 如果该分类不在字典中,创建该分类
classCount[vote] = 0
# 在不在字典中都要+1
classCount[vote] += 1
# 按投票次数降序排列
sortedClassCount = sorted(
classCount.items(), key=classCount.items(1), reverse=True)
# 选择最多出现的键值
return sortedClassCount[0][0]
给定数据和特征 生成决策树
def createTree(dataSet, labels):
# 利用函数的嵌套创建决策树,需要传入数据集和特征标签
# 即第二个叶子的创建步骤和第一个叶子一样,所以直接调用定义的函数
# 利用特征把数据集分开
classList = [example[-1] for example in dataSet]
# 将分类结果传入分类列表 labels是特征标签
if classList.count(classList[0]) == len(classList):
# 列表中某个分类出现的次数等于整个列表长度
# 即只有一种分类结果,直接返回该分类,递归函数停止
return classList[0]
# 已经甄别完所有特征即剔除特征后只剩下分类列
if len(dataSet[0]) == 1:
# 第一条数据只剩分类标签那一列
# 那么按照多数投票原则,返回该分类以及次数
return majorityCnt(classList)
# 最多的为类别,递归函数停止结束
# 如果能找到某些特征能够将数据集分开
# 遍历特征过程
bestFeat = chooseBestFeatureToSplit(dataSet)
# 选出第一好的特征是第几个
bestFeatLabel = labels[bestFeat]
# 从列表标签中选出具体的特征
myTree = {bestFeatLabel: {}}
# 设置成递归的结构,字典中的第一个key设置为第一好的特征
# 值为一个字典要进行下一次分类,树结构本质是一个字典
del(labels[bestFeat])
# 从特征表中删去已经分类好的特征
bestFeatValue = [example[bestFeat] for example in dataSet]
uniqueValue = set(bestFeatValue)
# 得到该特征的所有相异取值,假设有n种取值
for value in uniqueValue:
sublabels = labels[:]
# 列表为可变数据 所以复制一个新列表进行递归
# 保证原标签不变,虽然第一次减去一个标签
# 但是后面传入的都是sublabel
myTree[bestFeatLabel][value] = createTree(
splitDataSet(dataSet, bestFeat, value), sublabels)
# 按最好特征的值将数据剥离成不同的集合这样就把数据分开了
# 得到的每一个节点下面都有一类数据,然后对每一类数据创建
# 决策树
# 最后返回创建好的决策树
return myTree
决策树可视化
decisionNode = dict(boxstyle='sawtooth', fc='0.8')
# 内部节点是方框
leafNode = dict(boxstyle='round4', fc='0.8')
# 叶节点是圆框
# 描述决策节点和叶节点的形状和线宽以及线的箭头形状
arrow_args = dict(arrowstyle='<-')
# 放在外面便于不同函数调用
画箭头指向文本用于注释
def plotNode(nodeText, centerpt, parentpt, nodeType):
# 注释决策树节点, 内容,文本箭头终点,箭头起点,文本框形式
createPlot.ax1.annotate(
nodeText, xy=parentpt, xycoords='axes fraction',
xytext=centerpt, textcoords='axes fraction',
ha='center', va='center', bbox=nodeType,
arrowprops=arrow_args)
# CSDN查询matplotlib的anotation即可
# ha为文本横坐标文本框哪个位置,va为纵坐标,这里是居中
# coords是指参考系
生成决策树图
def createPlot(inTree):
# 设置散点图的初始值
fig = plt.figure(1, facecolor='white')
# 创建散点图
fig.clf()
axprops = dict(xticks=[], yticks=[])
# 将x,y 设置为字典,然后传入散点图子图
createPlot.ax1 = plt.subplot(111, frameon=False, **axprops)
# 111第一个图,无边框
# 计算intree的宽度和高度
plotTree.totalW = float(getNumberleafs(inTree))
plotTree.totalD = float(getDepth(inTree))
# 初始化偏离起点即分支点运动起点
# 我们要确定每个节点放置的位置
plotTree.Xoff = -0.5 / plotTree.totalW
# 以0.5为中心,向左偏离
plotTree.Yoff = 1.0
plotTree(inTree, (0.5, 1), '')
# 首先递归函数传入(0.5,1) 和 没有任何形式的文本框
# 调用的是下面的函数
plt.show()
def plotTree(myTree, parentpt, nodeText):
# 以(0.5,1)和 空文本框开始
# 无centerpt 起点parentpt 文本框形式
numberleafs = getNumberleafs(myTree)
# 计算整个树的宽即叶节点数
# 根据深度和宽度画决策树
firstStr = list(myTree.keys())[0]
# 选出第一个特征
cntrpt = (
plotTree.Xoff + 1.0*(1.0 + numberleafs)/2.0/plotTree.totalW,
plotTree.Yoff
)
# 原始一个叶子所占宽度*0.5为原来半个叶子宽度
# 即每次向左偏离半个叶子,第一次为新树半数叶子+半个叶子所占宽度即中心
# 主要保证每个根节点位于该树所有叶节点的中心
# 比如对称树的话 第二次节点数可能只有1/4
plotMidText(cntrpt, parentpt, nodeText)
# 做标记在两个中间(初始nodeText为空)
plotNode(firstStr, cntrpt, parentpt, decisionNode)
# 内容,终点,起点,节点,从根节点往下画
# 但是注意到两个点是重合的,而且y没有变化,所以没有箭头只有框和字符
# 这样画出第一个决策点,处于正中间
secondDict = myTree[firstStr]
# 第二个字典 包括第一个特征的取值和下面的树
plotTree.Yoff = plotTree.Yoff - 1.0/plotTree.totalD
# 计算y轴的偏离 与1偏离,简单更新即可,不想x轴需要考虑众多节点
for key in secondDict.keys():
# 遍历第一层的每条分支,即特征的每个取值
if type(secondDict[key]).__name__ == 'dict':
# 如果是字典即树,继续按上面步骤画图
plotTree(secondDict[key], cntrpt, str(key))
# 但是这时cntrpt已经是更新后的坐标,而且y也更新
# 并且在画节点时,作为起点,画图时新的cntrpt作为
# 终点,处于中心并且y下沉的地方
# 如果两个值都是字典即树呢
# 在不改变y的情况下,将x右移一半该新树节点的位置
# 保证每个树枝左边或者右边至少能放下一半节点
# 如果是字典即树每次画完箭头还要更新y的位置
else:
plotTree.Xoff = (
plotTree.Xoff + 1.0/plotTree.totalW)
# 对于的值是如果是叶节点,那么向右偏离半个节点宽度
# 因为下面不用放节点了所以不考虑下面的节点数
# 所以也不用更新y的位置
plotNode(secondDict[key], (
plotTree.Xoff, plotTree.Yoff), cntrpt,
leafNode)
# 图也要自己画,没有递归函数
plotMidText(
(plotTree.Xoff, plotTree.Yoff), cntrpt,
str(key)
)
# 标记也要自己画
# 标记的是 键 0 1 3
plotTree.Yoff = plotTree.Yoff + 1.0/plotTree.totalD
可视化判断结果 放在直线上
def plotMidText(cntrpt, parentpt, textString):
xMid = ((parentpt[0]-cntrpt[0]))/2.0 + cntrpt[0]
yMid = ((parentpt[1]-cntrpt[1]))/2.0 + cntrpt[1]
createPlot.ax1.text(xMid, yMid, textString)
# 将注释放在两个文本框的中间 即0/1/2/3
计算叶节点数
def getNumberleafs(myTree):
# 为了获得树高和叶节点的数量用来绘制整个决策树
numberleafs = 0
firstStr = list(myTree)[0]
# 找出第一个特征
secondDict = myTree[firstStr]
# 找出第二个特征
for key in secondDict.keys():
# 遍历第一个特征的值,如果是字典那么进入下一次递归
if type(secondDict[key]).__name__ == 'dict':
numberleafs += getNumberleafs(secondDict[key])
else:
# 如果不是字典,那么节点不再分,叶节点数+1
numberleafs += 1
# 递归把原来叶子加上新字典的叶子
# 所有树都要计算
return numberleafs
计算深度或者高度
def getDepth(myTree):
# 得到树的高度 默认可以分一层
maxdepth = 0
firstStr = list(myTree)[0]
secondDict = myTree[firstStr]
# 得到第一个特征的值
for key in secondDict.keys():
# 遍历每一个特征的值,若是是字典进行递归
if type(secondDict[key]).__name__ == 'dict':
numdepth = 1 + getDepth(secondDict[key])
# 如果是字典,那么1+该字典的深度
# 如果还是字典,那么1+1+该字典的深度
# 直到不是字典 1+1+1,所以只要还有树结构即内部节点
# 深度计算就不停止,直到叶节点停止并且在原来基础上+1
else:
numdepth = 1
if numdepth > maxdepth:
maxdepth = numdepth
# 每次递归都要改变maxdepth的值
# 只是一个计数变量
return maxdepth
储存生成的树
def retrieveTree(i):
# 将树储存到一个函数中避免每次都要创建树,设置两个常用的树
listOftrees = [
{
'no surfacing':
{
0: 'no', 1:
{
'flippers': {0: 'no', 1: 'yes'}}}},
{'no surfacing': {
0: 'no', 1:
{
'fippers': {
0: {'head': {0: 'no', 1: 'yes'}},
1: 'yes'}
}, 3: 'maybe'}}
]
return listOftrees[i]
利用树进行分类
def classify(inputTree, featLabels, testVec):
# 如何分类,比较键是否与对应值相同
firstStr = list(inputTree.keys())[0]
# 仍然是找到第一个特征
secondDict = inputTree[firstStr]
# 第一层下面的所有东西都是第二字典
featIndex = featLabels.index(firstStr)
# 计算出决策树中的第一个特征(选出来的)
# 在测试特征向量中的索引(给定的)
for key in secondDict.keys():
# 这里的key是0,1,2等
if testVec[featIndex] == key:
# 特征标签(给定)和测试样本(向量)一一对应
# 对于某个节点:是一个特征,该特征在给定特征向量
# 对应的索引也对应样本中的某个特征
# 如果测试向量特征的值和节点的键(0/1)相同
# 则归入一类
# 如果这个键的值是字典则继续迭代
if type(secondDict[key]).__name__ == 'dict':
classLabel = (
classify(
secondDict[key], featLabels,
testVec))
# 直到出现叶节点,则归入那一类
# 只有定义classLabel最后才会返回
else:
classLabel = secondDict[key]
# 如果不是字典,那么分类结果就是键对应的值
return classLabel
树的储存和读取
def storeTree(inputTree, filename):
# 将树写入文件
with open(filename, 'w') as fw:
# open() 函数r是读取,w是写入
pickle.dump(inputTree, fw)
# 封装方法
def grabTree(filename):
# 提取树文件
with open(filename) as fr:
read = pickle.load(fr)
# 读取方法
return read
最后
以上就是文静芝麻为你收集整理的自学机器学习实战(peter harrington)决策树及其可视化的python代码练习数据集和特征标签计算集合的信息(熵)抽样:抽取某个特征取定值的数据集根据信息增益选出最好的特征多数投票原则给定数据和特征 生成决策树决策树可视化画箭头指向文本用于注释生成决策树图可视化判断结果 放在直线上计算叶节点数计算深度或者高度储存生成的树利用树进行分类树的储存和读取的全部内容,希望文章能够帮你解决自学机器学习实战(peter harrington)决策树及其可视化的python代码练习数据集和特征标签计算集合的信息(熵)抽样:抽取某个特征取定值的数据集根据信息增益选出最好的特征多数投票原则给定数据和特征 生成决策树决策树可视化画箭头指向文本用于注释生成决策树图可视化判断结果 放在直线上计算叶节点数计算深度或者高度储存生成的树利用树进行分类树的储存和读取所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复