本文转自Jack Cui http://cuijiahua.com/blog/2017/11/ml_3_decision_tree_2.html#
ID3算法:
核心就是在决策树各个结点上对应信息增益准则选择特征,递归的构造决策树。具体方法是:从根结点开始,对结点计算所有可能的特征信息增益,选择信息增益最大的特征作为结点特征,由该特征的不同取值建立子节点;再对子节点递归地调用以上方法,构建决策树;直到所有特征的信息增益均很小或没有特征可以选择为止。最后得到一个决策树。
import operator
#计算数据集的香农熵
def calcShannonEnt(dataset):
numEntries=len(dataset)#求数据集列表的行数
labelCounts={}
for featVec in dataset:
currentLabel=featVec[-1]
#如果该键不在字典中,则向字典中添加该键并赋值为0
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)
return shannonEnt
#创建测试数据集
def createDataSet():
dataSet=[[0, 0, 0, 0, 'no'],
[0, 0, 0, 1, 'no'],
[0, 1, 0, 1, 'yes'],
[0, 1, 1, 0, 'yes'],
[0, 0, 0, 0, 'no'],
[1, 0, 0, 0, 'no'],
[1, 0, 0, 1, 'no'],
[1, 1, 1, 1, 'yes'],
[1, 0, 1, 2, 'yes'],
[1, 0, 1, 2, 'yes'],
[2, 0, 1, 2, 'yes'],
[2, 0, 1, 1, 'yes'],
[2, 1, 0, 1, 'yes'],
[2, 1, 0, 2, 'yes'],
[2, 0, 0, 0, 'no']]
labels=["年龄","有工作","有自己的房子","信贷情况"]
return dataSet,labels
#按照给定的特征划分数据集
def splitDataSet(dataSet,axis,value):
retDataSet=[]
for featVec in dataSet:
if featVec[axis]==value:
reducedFeatVec=featVec[:axis]
#注意extend和append的区别
reducedFeatVec.extend(featVec[axis+1:])#[]
retDataSet.append(reducedFeatVec)#[[]]
return retDataSet
#选择最优特征
def chooseBestFeatureToSplit(dataSet):
numFeatures=len(dataSet[0])-1
bestInfoGain=0.0
bestFeature=-1
for i in range(numFeatures):
#当i=0时,[0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2]
featList=[example[i] for example in dataSet]
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=shannon-newEntropy
if(infoGain>bestInfoGain):
bestInfoGain=infoGain
bestFeature=i
return bestFeature
#统计classList中出现次数最多的元素
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,featLabels):
classList=[example[-1] for example in dataSet]
if classList.count(classList[0])==len(classList):
return classList[0]
#遍历完所有特征时返回出现次数最多的类标签
#它的作用是使用完了所有特征,仍然不能将数据集划分成仅包含唯一类别的分组。
#此时使用majorityCnt函数挑选出现次数多的类别作为返回值。
#如果没有调用它,则因为这个训练集可以直接划分成仅包含唯一类别的分组,所以不会触发这个终止条件。
if len(dataSet[0])==1:
return majorityCnt(classList)
bestFeat=chooseBestFeatureToSplit(dataSet)
bestFeatLabel=labels[bestFeat]
featLabels.append(bestFeatLabel)
myTree={bestFeatLabel:{}}
del(labels[bestFeat])#删除已经使用过的特征标签
featValues=[example[bestFeat] for example in dataSet]#得到训练集中最优特征的属性值
uniqueVals=set(featValues)#去重
for value in uniqueVals:
myTree[bestFeatLabel][value]=createTree(splitDataSet(dataSet,bestFeat,value),labels,featLabels)
return myTree
dataSet,labels=createDataSet()
shannon=calcShannonEnt(dataSet)
featLabels=[]
myTree=createTree(dataSet,labels,featLabels)
print(myTree)
用决策树分类:比较测试数据与决策树上的数值,递归执行该过程直到进入叶子结点;最后将测试数据定义为叶子结点所属的类型
#决策树分类
def classify(inputTree,featLabels,testVec):
firstStr=next(iter(inputTree))#有自己的房子,有工作
secondDict=inputTree[firstStr]
featIndex=featLabels.index(firstStr)
print("featIndex:",featIndex)
for key in secondDict.keys():
if testVec[featIndex]==key:
if type(secondDict[key]).__name__=='dict':
classLabel=classify(secondDict[key],featLabels,testVec)
else:
classLabel=secondDict[key]
return classLabel
dataSet,labels=createDataSet()
shannon=calcShannonEnt(dataSet)
featLabels=[]
myTree=createTree(dataSet,labels,featLabels)
print(myTree)
testVec=[0,1]
result=classify(myTree,featLabels,testVec)
if result=='yes':
print("放贷")
if result=='no':
print("不放贷")