"""
优点:计算复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可以处理不相关特征数据
缺点:可能会产生过度匹配问题
运用数据类型:数值型和标称型
决策树的一般流程:
1.收集数据:可以使用任何方法
2.准备数据:树构造算法只适用于标称型数据,因此数值型数据必须离散化
3.分析数据:可以使用任何方法,构造树完成后,我们应该检查图形是否符合预期
4.训练算法:构造树的数据结构
5.测试算法:使用经验树计算错误率
6.使用算法:此步骤可以适用于任何监督学习算法,而使用决策树可以更好地理解数据的内在含义
"""
from math import log
import operator
def calcShannonEnt(dataSet):
"""
该方法计算给定数据集的香农熵
:param dataSet:
:return:
"""
numEntries = len(dataSet) # 计算数据集中实例的总数
labelCounts = {} # 字典dict
# 为所有可能分类创建字典
for featVec in dataSet:
currentLabel = featVec[-1] # -1下标可获取最后一个元素的值 yes yes no no no
if currentLabel not in labelCounts.keys():
labelCounts[currentLabel] = 0
labelCounts[currentLabel] += 1
# 计算香农熵,以2为底求对数
shannonEnt = 0.0
for key in labelCounts:
prob = float(labelCounts[key])/numEntries # 选择该分类的概率
shannonEnt -= prob * log(prob, 2) # 计算香农熵
return shannonEnt
"""
得到熵之后,我们就可按照获取最大信息增益的方法划分数据集,如何划分数据集以及如何度假来哪个信息增益?
分类算法除了需要测量信息熵,还需要划分数据集,度量划分数据集的熵,以便判断
当前是否正确地划分了数据集,我们将对每个特征划分数据集的结果计算一次信息熵,然后判断
按照哪个特征划分数据集是最好的方式,想象一个分布在二维空间的数据散点图,需要在数据之间画条线
将他们分成两部分,我们应该按照x周还是y轴的
"""
def splitDataSet(dataSet, axis, vallue):
"""
按照给定的特征划分数据集
在python中,在函数内部对列表对象的修改,将会影响该列表对象的整个生存周期,
为了消除这个不良影响,我们需要在函数的开始声明一个新列表对象,因为该函数嗲吗在同一
数据集上被调用多次,为了不修改原始数据集,我们需要在函数的开始声明一个新的列表对象
数据集这个列表中的各个元素也是列表,我们要遍历数据集中的每个元素,一旦发现符合要求的值
则将其添加到新创建的列表中
:param dataSet:待划分的数据集
:param axis:划分数据集的特征
:param vallue:需要返回的特征的值
:return:
"""
retDataSet = [] # 创建新的list对象,
for featVec in dataSet: # 数据集中的元素仍为列表,featVec的值也是列表
if featVec[axis] == vallue: # 如果列表元素的axis位置的元素的值与value相等
reducedFeatVec = featVec[:axis] # reducedFeatVec为列表类型,如featVec[:3]取值为下表0-2的值
reducedFeatVec.extend(featVec[axis+1:]) # 注意extend和append的区别:a.extend(b) = [1, 2, 3, 4, 5, 6]
retDataSet.append(reducedFeatVec) # a = [1, 2, 3] b = [4, 5, 6] a.append(b)结果为:[1, 2, 3, [4, 5, 6]]
return retDataSet
"""
接下来我们将遍历整个数据集,循环计算香农熵和splitDataSet()函数,找到最好的特征
划分方式,熵计算将会告诉我们如何划分数据集是最好的数据组织方式
"""
def chooseBestFeatureToSplit(dataSet):
"""
选择最好的数据集划分方式
在函数中调用的数据必须满足一定的要求,第一个要求是,数据必须是一种由列表元素
组成的列表,而且所有的列表元素都要具有相同的数据长度,第二个要求是,数据的最后一列
或者每个实例的最后一个元素是当前实例的类别标签,数据集一但满足上述要求,我们就可以在
函数第一行判断当前数据集包含多少种特征属性。
:param dataSet:
:return:
"""
numFeatures = len(dataSet[0]) - 1 # 判断当前数据集包含多少种特征属性
baseEntropy = calcShannonEnt(dataSet) # 计算原始香农熵,保存最初的无序度量值,用于划分完之后的数据集计算的熵值进行比较
bestInfoGain = 0.0
bestFeature = -1
# 该for循环遍历数据集的所有特征
for i in range(numFeatures):
featList = [example[i] for example in dataSet] # 将数据集中所有第i个特征值或者所有可能存在的值写入这个新list中
uniqueVals = set(featList) # set:集合数据类型,集合数据类型与列表类型相似,不同之处在于集合类型中的每个值互补相同,从列表中创建集合是python语言得到列表中唯一元素值的最快方法
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
if (infoGain > bestInfoGain):
bestInfoGain = infoGain
bestFeature = i
return bestFeature
"""
得到原始数据集,然后基于最好的属性值划分数据集,由于特征值可能多于两个,因此
可能存在大于两个分支的数据集划分,第一次划分之后,数据将被向下传递到树分支的下
一个节点,在这个节点上,我们再次划分数据,因此我们可以采用递归的原则处理数据
"""
def majorityCnt(classList):
"""
多数表决
:param classList:
:return:
"""
classCount = {} # 数据字典
for vote in classList:
# 如果不存在,就添加到列表中
if vote not in classCount.keys():
classCount[vote] = 0
classCount[vote] += 1 # 字典对象存储了classList中每个类标签出现的频率,利用operator操作键值排序字典,并返回出现次数最多的分类名称
sortedClassCount = sorted(classCount.iteritems(), key = operator.itemgetter(1), reversed = True)
return sortedClassCount[0][0]
def createTree(dataSet, labels):
classList = [example[-1] for example in dataSet] # 取数据集最后一列元素
# 递归函数的第一个停止条件:所有类标签完全相同,则直接返回类标签
if classList.count(classList[0]) == len(classList): # count方法返回元素在列表中出现的次数
return classList[0]
# 递归函数的第二个停止条件:使用完了所有特征,仍然不能将数据集划分成仅包含唯一类别的分组
if len(dataSet[0]) == 1:
return majorityCnt(classList)
bestFeat = chooseBestFeatureToSplit(dataSet) # 当前数据集选取的最好特征存储在bestFeat中
bestFeatLabel = labels[bestFeat]
myTree = {bestFeatLabel:{}} # 字典变量存储了树所有的信息
del(labels[bestFeat])
featValues = [example[bestFeat] for example in dataSet]
uniqueVals = set(featValues)
for value in uniqueVals:
subLabels = labels[:]
myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), subLabels)
return myTree
if __name__ == '__main__':
dataSet = [[1, 1, "yes"],
[1, 1, "yes"],
[1, 0, "no"],
[0, 1, "no"],
[0, 1, "no"]]
labels = ["no surfacing", "flippers"]
myTree = createTree(dataSet, labels)
print(myTree)
决策树判断海洋生物是否为鱼类
最新推荐文章于 2020-05-06 15:13:24 发布