决策树的构造:
优点:计算复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可以处理不相关特征数据。
缺点:可能会产生过度匹配的问题
实用数据类型:数值型和标称型
一般流程:
- 收集数据:
- 准备数据:数值型数据必须离散化
- 分析数据:可以使用任何方法,构造树完成之后,我们应该检查图形是否符合预期
- 训练算法:构造树的数据结构
- 测试算法:使用经验树计算错误率
- 使用算法
信息增益:在划分数据集之前之后信息发生的变化称为信息增益。
计算给定数据集的香农熵(信息的期望值)
P是选择该分类的概率
ps.书上的代码有歧义
from math import log
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):
numEntries = len(dataSet)
labelCounts = {}
for featVec in dataSet: #为所有可能分类创造字典
currentLable = featVec[-1] #创建一个数据字典,它的键值是最后一列的数值
##统计当前类别出现的次数,利用字典的自动添加功能,添加值为0的项
if currentLable not in labelCounts.keys():
labelCounts[currentLable] = 0 #归0重新计算一类的标签数
labelCounts[currentLable] += 1 #计算一类的标签数
shannonEnt = 0.0
for key in labelCounts:
prob = float(labelCounts[key])/numEntries #使用所有类标签的发生频率计算类别出现的概率
shannonEnt -= prob * log(prob,2) #以2为底求对数
return shannonEnt
dataSet , labels = createDataSet()
print(calcShannonEnt(dataSet))
调用得到:
按照给定特征划分数据集:
def splitDataSet(dataSet, axis, value): #待划分的数据集,划分数据集的特征,特征的返回值
retDataSet = []
for festVec in dataSet:
if festVec[axis] == value:
reducedFeatVec = festVec[:axis]
reducedFeatVec.extend(festVec[axis+1:])
retDataSet.append(reducedFeatVec)
return retDataSet
extend方法和attend方法不同:
a.append(b)方法列表得到的新元素b也是一个列表
a.extend(b)方法得到一个包含a和b所有元素的列表
选择最好的数据集划分方式:
def chooseBestFeatureTopSplit(dataSet):
numFeature = len(dataSet[0]) - 1
baseEntropy = calcShannonEnt(dataSet)
bestInfoGain = 0.0; bestFeature = -1
for i in range(numFeature):
featList = [example[i] for example in dataSet] #将所有第i个特征值或者所有可能存在的值写入这个新List
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
#计算最好的信息增益
if(infoGain > bestInfoGain):
bestInfoGain = infoGain
bestFeature = i
return bestFeature
在函数中调用的数据需要满足一定的要求:
第一个要求是,数据必须是一种由列表元素组成的列表,而且所有的列表元素都要具有相同的数据长度。
第二个要求,数据的最后一列或者每个实例的最后一个元素是当前实例的类别标签。
featList = [example[i] for example in dataSet]
#将dataSet中的数据先按行依次放入example中,然后取得example中的example[i]元素,放入列表featList中
递归构建决策树:
递归结束的条件是:程序遍历完所有划分数据集的属性,或者每个分支下的所有实例都具有相同的分类。如果所有实例具有相同的分类,则得到一个叶子结点或者终止块。任何到达叶子结点的数据必然属于叶子结点的分类。
如果数据集已经处理了所有属性,但是类标签依然不是唯一的,此时通常采用多数表决的方法决定该叶子结点的分类。
def majorityCnt(classList):
classCount = {}
for vote in classList:
if vote not in classCount.keys():
classCount[vote] = 0
classCount[vote] += 1
sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)
return sortedClassCount[0][0]
创建树的函数代码:
def createTree(dataSet, labels): #标签列表包含了数据集中所有特征的标签
classList = [example[-1] for example in dataSet] #读取最后一列类标签列表
#递归结束条件1
if classList.count(classList[0]) == len(classList):
return classList[0]
#classlist以第一项元素的标签计数,如果等于列表个数,说明列表中值都是一样的
#递归结束条件2
if len(dataSet[0]) == 1:
return majorityCnt(classList)
#递归终止的另一情况:已经划分完所有属性特征
#len(dataSet[0])==1表示划分到最后只剩下一列类别标签
bestFeat = chooseBestFeatureTopSplit(dataSet)
#针对当前数据集选择最优划分特征
bestFeatLabel = labels[bestFeat]#最优特征的标签
myTree = {bestFeatLabel:{}}
del(labels[bestFeat]) #将已选维度标签从标签列表中删除
featValues = [example[bestFeat] for example in dataSet]#得到列表包含的所有属性值
uniqueVals = set(featValues) #去掉重复的属性值
for value in uniqueVals:
#对最优维度的每个值进行操作,一个值对应一个分支
subLabels = labels[:]
#复制获得子维度标签,此时labels中已删除当前最优维度标签
myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels)
#递归生成树
return myTree
dataSet=[ [1,1,'yes'],
[1,1,'yes'],
[1,0,'no'],
[0,1,'no'],
[0,1,'no']]
>>>featList=[example[1] for example in dataSet]
>>>featList
[1, 1, 0, 1, 1]
>>>featList=[example[0] for example in dataSet]
>>>featList
[1, 1, 1, 0, 0]
>>>featList=[example[2] for example in dataSet]
>>>featList
['yes', 'yes', 'no', 'no', 'no']