文章原创,最近更新:2018-08-20
本章节的主要内容是:
重点介绍项目案例2:决策树预测眼镜类型代码汇总。
1.决策树项目案例介绍:
项目案例2:
决策树预测眼镜类型
项目概述:
隐形眼镜类型包括硬材质、软材质以及不适合佩戴隐形眼镜。我们需要使用决策树预测患者需要佩戴的隐形眼镜类型。
开发流程:
- 收集数据:提供的文本文件。
- 准备数据:解析tab键分隔的数据行。
- 分析数据:快速检查数据,确保正确地解析数据内容,使用createPlot函数绘制最终的树形图.
- 训练算法:使用createTree()函数.
- 测试算法:編写测试函数验证决策树可以正确分类给定的数据实例。
- 使用算法:存储树的数据结构,以便下次使用时无需重新构造树.
数据集介绍
隐形眼镜数据集是非常著名的数据集,它包含很多患者眼部状况的观察条件以及医生推荐的隐形眼镜类型。隐形眼镜类型包括硬材质、软材质以及不适合佩戴隐形眼镜。
2.完整的代码
from math import log
import operator
def calcShannonEnt(dataSet):
# 获取数据集dataSet列表的长度,表示计算参与训练的数据量
numEntries=len(dataSet)
# 新建一个空字典labelCounts,用以统计每个标签出现的次数,进而计算概率
labelCounts={}
for featVec in dataSet:
# featVec[-1]获取了daatSet中每行最后一个数据,作为字典中的key(标签)
currentLabel = featVec[-1]
# 以currentLabel作为key加入到字典labelCounts.
# 如果当前的键值不存在,则扩展字典并将当前键值加入字典。每个键值都记录了当前类别出现的次数。
# 键值存在则则对应value+1,否则为0
if currentLabel not in labelCounts.keys():
labelCounts[currentLabel]=0
labelCounts[currentLabel] += 1
# 对于 label 标签的占比,求出 label 标签的香农熵
shannonEnt = 0.0
for key in labelCounts:
# 计算分类概率prob=标签发生频率,labelCounts[key]除以数据集长度numEntries
prob = float(labelCounts[key])/numEntries
# 计算香农熵,以2为底求对数
shannonEnt -=prob * log(prob,2)
return shannonEnt
def splitDataSet(dataSet,axis,value):
"""
splitDataSet(通过遍历dataSet数据集,求出index对应的column列的值为value的行)
就是依据index列进行分类,如果index列的数据等于value的时候,就要index划分到我们创建的新的数据集中
Args:
dataSet:数据集 待划分的数据集
axis:表示每一行的index列 特征的坐标,等于0,第0个特征为0或者1
value:表示index列对应的value值 需要返回的特征的值
Returns:
index列为value的数据集[该数据集需要排除axis列]
"""
retDataSet = []
# index列为value的数据集[该数据集需要排除index列]
# 判断index列的值是否等于value
# 遍历数据集,将axis上的数据和value值进行对比
for featVec in dataSet:
# 如果待检测的特征axis和指定的特征value相等
if featVec[axis] == value:
# 从第0开始,一旦发现第axis符合要求,就将数据0-axis保存至reduceFeatVec
reducedFeatVec =featVec[:axis]
# 将指定的数据的axis+1位到末尾添加至reducedFeatVec,保持数据完整性
reducedFeatVec.extend(featVec[axis+1:])
# 收集结果值除掉index列的reducedFeatVec收据集添加到retDataSet数据集
retDataSet.append(reducedFeatVec)
return retDataSet
def chooseBestFeatTopSplit(dataSet):
"""chooseBestFeatureToSplit(选择最好的特征)
Args:
dataSet 数据集
Returns:
bestFeature 最优的特征列
"""
# 求第一行有多少列的 Feature, 减去1,是因为最后一列是label列
numFeatures = len(dataSet[0])-1
# 计算没有经过划分的数据的香农熵
baseEntropy = calcShannonEnt(dataSet)
# 最优的信息增益值
bestInfoGain = 0.0
#最优的Featurn编号
bestFeature = -1
for i in range(numFeatures):
# 创建唯一的分类标签列表,获取第i个的所有特征(信息元纵排列!)
featList = [example[i] for example in dataSet]
"""
print(featList)结果为
[1, 1, 1, 0, 0]
[1, 1, 0, 1, 1]
"""
# 使用set集,排除featList中的重复标签,得到唯一分类的集合
uniqueVals = set(featList)
"""
print(uniqueVals)结果为
{0, 1}
{0, 1}
"""
newEntropy = 0.0
# 遍历当次uniqueVals中所有的标签value(这里是0,1)
for value in uniqueVals:
# 对第i个数据划分数据集, 返回所有包含i的数据(已排除第i个特征)
subDataSet = splitDataSet(dataSet, i, value)
"""
print(subDataSet)结果为
[[1, 'no'], [1, 'no']]
[[1, 'yes'], [1, 'yes'], [0, 'no']]
[[1, 'no']]
[[1, 'yes'], [1, 'yes'], [0, 'no'], [0, 'no']]
"""
# 计算包含个i的数据占总数据的百分比
prob = len(subDataSet)/float(len(dataSet))
"""
print(prob)结果为
0.4
0.6
0.2
0.8
"""
# 计算新的香农熵,不断进行迭代,这个计算过程仅在包含指定特征标签子集中进行
newEntropy += prob * calcShannonEnt(subDataSet)
"""
print(calcShannonEnt(subDataSet))
0.0
0.9182958340544896
0.0
1.0
print(newEntropy)结果为
0.0
0.5509775004326937
0.0
0.8
"""
# 计算信息增益
infoGain = baseEntropy - newEntropy
# 如果信息增益大于最优增益,即新增益newEntropy越小,信息增益越大,分类也就更优(分类越简单越好)
"""
print(infoGain)结果为
0.4199730940219749
0.17095059445466854
"""
if (infoGain > bestInfoGain):
# 更新信息增益
bestInfoGain = infoGain
# 确定最优增益的特征索引
bestFeature = i
# 更新信息增益
# 返回最优增益的索引
return bestFeature
def majorityCnt(classList):
"""
majorityCnt(筛选出现次数最多的分类标签名称)
Args:
classList 类别标签的列表
Returns:
sortedClassCount[0][0] 出现次数最多的分类标签名称
假设classList=['yes', 'yes', 'no', 'no', 'no']
"""
classCount={}
for vote in classList:
if vote not in classCount.keys():classCount[vote]= 0
classCount[vote] += 1
"""
print(classCount[vote])的结果为:
{'yes': 1}
{'yes': 2}
{'yes': 2, 'no': 1}
{'yes': 2, 'no': 2}
{'yes': 2, 'no': 3}
"""
sortedClassCount =sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
"""
print(sortedClassCount)的结果为:
[('no', 3), ('yes', 2)]
"""
return sortedClassCount[0][0]
# 创建树的函数代码
def createTree(dataSet, labels):
"""
createTree(创建树)
Args:
dataSet 数据集
labels 标签列表:标签列表包含了数据集中所有特征的标签。最后代码遍历当前选择
Returns:
myTree 标签树:特征包含的所有属性值,在每个数据集划分上递归待用函数createTree(),
得到的返回值将被插入到字典变量myTree中,因此函数终止执行时,字典中将会嵌套很多代
表叶子节点信息的字典数据。
"""
#取得dataSet的最后一列数据保存在列表classList中
classList = [example[-1] for example in dataSet]
#如果classList中的第一个值在classList中的总数等于长度,也就是说classList中所有的值都一样
#也就等价于当所有的类别只有一个时停止
if classList.count(classList[0])==len(classList):
return classList[0]
#当数据集中没有特征可分时也停止
if len(dataSet[0])==1:
#通过majorityCnt()函数返回列表中最多的分类
return majorityCnt(classList)
#通过chooseBestFeatTopSplit()函数选出划分数据集最佳的特症
bestFeat = chooseBestFeatTopSplit(dataSet)
#最佳特征名 = 特征名列表中下标为bestFeat的元素
bestFeatLabel=labels[bestFeat]
# 构造树的根节点,多级字典的形式展现树,类似多层json结构
myTree={bestFeatLabel:{}}
# 删除del列表labels中的最佳特征(就在labels变量上操作)
del(labels[bestFeat])
#取出所有训练样本最佳特征的值形成一个list
featValues = [example[bestFeat] for example in dataSet]
# 通过set函数将featValues列表变成集合,去掉重复的值
uniqueVals = set(featValues)
for value in uniqueVals:
#复制类标签并将其存储在新列表subLabels中
subLabels = labels[:]
myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels)
return myTree
# 打开数据集文件
fr=open("lenses.txt")
# split()返回分割后的字符串列表
lenses=[inst.strip().split("\t") for inst in fr.readlines()]
"""
print(fr.readlines())的结果如下:
['young\tmyope\tno\treduced\tno lenses\n', 'young\tmyope\tno\tnormal\tsoft\n', 'young\tmyope\tyes\treduced\tno lenses\n', 'young\tmyope\tyes\tnormal\thard\n', 'young\thyper\tno\treduced\tno lenses\n', 'young\thyper\tno\tnormal\tsoft\n', 'young\thyper\tyes\treduced\tno lenses\n', 'young\thyper\tyes\tnormal\thard\n', 'pre\tmyope\tno\treduced\tno lenses\n', 'pre\tmyope\tno\tnormal\tsoft\n', 'pre\tmyope\tyes\treduced\tno lenses\n', 'pre\tmyope\tyes\tnormal\thard\n', 'pre\thyper\tno\treduced\tno lenses\n', 'pre\thyper\tno\tnormal\tsoft\n', 'pre\thyper\tyes\treduced\tno lenses\n', 'pre\thyper\tyes\tnormal\tno lenses\n', 'presbyopic\tmyope\tno\treduced\tno lenses\n', 'presbyopic\tmyope\tno\tnormal\tno lenses\n', 'presbyopic\tmyope\tyes\treduced\tno lenses\n', 'presbyopic\tmyope\tyes\tnormal\thard\n', 'presbyopic\thyper\tno\treduced\tno lenses\n', 'presbyopic\thyper\tno\tnormal\tsoft\n', 'presbyopic\thyper\tyes\treduced\tno lenses\n', 'presbyopic\thyper\tyes\tnormal\tno lenses\n']
print(lenses)的结果如下:
[['young', 'myope', 'no', 'reduced', 'no lenses'], ['young', 'myope', 'no', 'normal', 'soft'], ['young', 'myope', 'yes', 'reduced', 'no lenses'], ['young', 'myope', 'yes', 'normal', 'hard'], ['young', 'hyper', 'no', 'reduced', 'no lenses'], ['young', 'hyper', 'no', 'normal', 'soft'], ['young', 'hyper', 'yes', 'reduced', 'no lenses'], ['young', 'hyper', 'yes', 'normal', 'hard'], ['pre', 'myope', 'no', 'reduced', 'no lenses'], ['pre', 'myope', 'no', 'normal', 'soft'], ['pre', 'myope', 'yes', 'reduced', 'no lenses'], ['pre', 'myope', 'yes', 'normal', 'hard'], ['pre', 'hyper', 'no', 'reduced', 'no lenses'], ['pre', 'hyper', 'no', 'normal', 'soft'], ['pre', 'hyper', 'yes', 'reduced', 'no lenses'], ['pre', 'hyper', 'yes', 'normal', 'no lenses'], ['presbyopic', 'myope', 'no', 'reduced', 'no lenses'], ['presbyopic', 'myope', 'no', 'normal', 'no lenses'], ['presbyopic', 'myope', 'yes', 'reduced', 'no lenses'], ['presbyopic', 'myope', 'yes', 'normal', 'hard'], ['presbyopic', 'hyper', 'no', 'reduced', 'no lenses'], ['presbyopic', 'hyper', 'no', 'normal', 'soft'], ['presbyopic', 'hyper', 'yes', 'reduced', 'no lenses'], ['presbyopic', 'hyper', 'yes', 'normal', 'no lenses']]
"""
# 建立四个标签
lensensLabels = ['age', 'prescript', 'astigmatic', 'tearRate']
# 创建决策树
lensesTree = createTree(lenses,lensensLabels)
# 输出看决策树结构
print(lensesTree)
输出结果如下:
{'age': {'young': {'prescript': {'myope': {'astigmatic': {'yes': {'tearRate': {'reduced': 'no lenses', 'normal': 'hard'}}, 'no': {'tearRate': {'reduced': 'no lenses', 'normal': 'soft'}}}}, 'hyper': {'astigmatic': {'yes': {'tearRate': {'reduced': 'no lenses', 'normal': 'hard'}}, 'no': {'tearRate': {'reduced': 'no lenses', 'normal': 'soft'}}}}}}, 'presbyopic': {'prescript': {'myope': {'astigmatic': {'yes': {'tearRate': {'reduced': 'no lenses', 'normal': 'hard'}}, 'no': 'no lenses'}}, 'hyper': {'astigmatic': {'yes': 'no lenses', 'no': {'tearRate': {'reduced': 'no lenses', 'normal': 'soft'}}}}}}, 'pre': {'prescript': {'myope': {'astigmatic': {'yes': {'tearRate': {'reduced': 'no lenses', 'normal': 'hard'}}, 'no': {'tearRate': {'reduced': 'no lenses', 'normal': 'soft'}}}}, 'hyper': {'astigmatic': {'yes': 'no lenses', 'no': {'tearRate': {'reduced': 'no lenses', 'normal': 'soft'}}}}}}}}