本文按照《机器学习实战》书中ID3算法构建决策树,构建步骤即为代码的先后顺序
def calcShannonEnt(dataSet): # 计算 熵 H
numEntries = len(dataSet)
labelCounts = {}
for featVec in dataSet: #the the number of unique elements and their occurance
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])/numEntries
shannonEnt -= prob * log(prob,2) #log base 2 shannonEnt = shannoEnt - prob * log(prob,2)
return shannonEnt
计算熵值
def createDataSet(): # 获取数据
dataSet = [[1, 1, 'yes'],
[1, 1, 'yes'],
[1, 0, 'no'],
[0, 1, 'no'],
[0, 1, 'no']]
# dataSet = [[1, 1, 'maybe'],
# [1, 1, 'yes'],
# [1, 0, 'no'],
# [0, 1, 'no'],
# [0, 1, 'no']]
labels = ['no surfacing','flippers']
#change to discrete values
return dataSet, labels
获取数据
'''
splitdataset函数 按照 axis参数对应的特征进行数据集的划分,返回划分后该特征等于value的值的子集
'''
def splitDataSet(dataSet, axis, value): # 按照给定特征划分数据集 参数为 待划分的数据集 划分数据集的特征 需要返回的特征的值 表示取出第axis列中值为value的数据集,并去除掉第axis列的数据。
retDataSet = []
for featVec in dataSet:
if featVec[axis] == value:
reducedFeatVec = featVec[:axis] # chop out axis used for splitting
reducedFeatVec.extend(featVec[axis + 1:])
retDataSet.append(reducedFeatVec)
return retDataSet
划分子集
# 信息增益 定义为 分裂前后的熵的变化。
def chooseBestFeatureToSplit(dataSet): # 获取 分类效果最好的 特征
numFeatures = len(dataSet[0]) - 1 #the last column is used for the labels 获取有多少特征属性
baseEntropy = calcShannonEnt(dataSet) # 计算 该数据集的 熵
bestInfoGain = 0.0; bestFeature = -1
for i in range(numFeatures): #iterate over all the features 遍历 所有的特征
featList = [example[i] for example in dataSet]#create a list of all the examples of this feature 取出每一条数据的各个特征
uniqueVals = set(featList) #get a set of unique values 获取每一个特征有哪些取值
newEntropy = 0.0
for value in uniqueVals: # 该循环执行 完成。 则 一个特征 完成所有子集的 计算
subDataSet = splitDataSet(dataSet, i, value) # 按照 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): # 取类别多的为该类别
classCount={}
for vote in 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]
def createTree(dataSet,labels):
classList = [example[-1] for example in dataSet] # ['yes','yes','no','no','no']
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:{}}
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
createTree函数递归创建树
mydat,labels=createDataSet()
print(mydat,labels)
mytree=createTree(mydat,labels)
print(mytree)
测试代码