决策树算法python实现
01.前言
决策树的特点
优点:计算复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可以处理不相关特征数据。
缺点:可能会产生过度匹配问题。
适用数据类型:数值类型和标称型。
当然决策树算法,从最初的id3发展到现在id4.5,其中的主要区别是在对信息度量方式的不同。这里主要依据ID3算法去划分数据集。
除此之外,我们还需要知道的知识就是信息增益。在划分数据集之前和之后,信息发生的变化称为信息增益,这时信息增益最高的特征就是最好的选则。这时我们就需要知道香农的信息论的一些知识–熵。这里可以参见自然语言处理中数学基础(信息论)
程序思想
使用python计算信息熵
#导包
from math import log
#计算熵的函数
def calcShannonEnt(dataSet):
#获取数据集的长度
numEntries=len(dataSet)
#定义一个字典,统计一个标签及其数量
labelCounts={}
#遍历数据集
for featVec in dataSet:
#获取数据集的标签
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)
return shannonEnt
#测试主函数
def main():
dataSet=[[1,1,'yes'],[1,0,'yes'],[1,0,'no'],[0,1,'no'],[0,1,'no']]
print(calcShannonEnt(dataSet))
if __name__ == '__main__':
main()
02.划分数据集
按照指定特征划分数据集,代码如下:
#参数说明:dataSet:待划分的数据集,axis:划分数据集的特征(索引),value:需要返回的特征的值
def splitDataSet(dataSet,axis,value):
#定义一个列表,存放划分好了的数据集
retDataSet=[]
#遍历数据集
for featVec in dataSet:
#判断一条数据是否是需要返回的值
if featVec[axis]==value:
#截取列表从索引为0到索引为axis
reducedFeatVec=featVec[:axis]
#将原来列表中索引为axis+1到结束放到截取的新列表中
reducedFeatVec.extend(featVec[axis+1:])
#将整个结果集放到要分离出特征的数据集中
retDataSet.append(reducedFeatVec)
return retDataSet
需要说明的是,这个函数的作用是:对数据集dataSet数据进行划分,取出数据集中索引为axis,标签值为value的所有数据。
这里还用使用了extend方法和append()方法,其中的区别可以参见python 列表自带extend()、append()的区别。
通过比较信息增益(可参考信息增益的计算)去找出最佳的数据集划分方式:
#选则最好的数据集划分方式
def chooseBestFeatureToSplit(dataSet):
#获取数据集的特征数,出去标签
numFeatures=len(dataSet[0])-1
#计算数据集的信息熵
baseEntropy=calcShannonEnt(dataSet)
#定义最佳信息增益
bestInfoGain=0.0
bestFeature=-1 #先默认为最后一个
for i in range(numFeatures):
#将所有数据集中的第i个特征放到一个list中
featList=[example[i] for example in dataSet]
#去重(获取数据集中有的对应特征有几个结果)
uniqueVals=set(featList)
newEntropy=0.0
#遍历,构建子数据集
for value in uniqueVals:
#将数据集中的子数据,去掉索引为i的特征,并且做一个匹配,返回一个只有vaule值得数据集
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
如果python中Set集合不清楚的可以查看python set()集合的使用
03.使用递归构建决策树
在此之前,我们需要清楚一点,如果在最后一个特征的时候,依然划分不出来的使用,这里我们就需要采取办法解决,就是在该特征情况下,取情况出现最多的那一个结果:
def majorityCnt(classList):
classCount={}
#遍历集合
for vote in classList:
if vote not in classCount.keys():classCount[vote]=0
classCount[vote]+=1
#利用operator操作键值排序
sortedClassCount=sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
return sortedClassCount[0][0]
这里需要导入:import operator
对于operator.itemgetter()使用不清楚的可以查见:python operator.itemgetter()函数的使用
创建决策树的函数:
#创建树的函数
def createTree(dataSet,labels):
#获取数据集的标签(最后一列数据)
classList=[example[-1] for example in dataSet]
#递归的第一个停止条件
#判断:如果数据集中一个标签的数量与这个标签集长度相同,就返回标签
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]
#使用set集合去除重复的值
uniqueVals=set(featValues)
#遍历所有最佳划分的分类标签
for value in uniqueVals:
#复制类标签
subLabels=labels[:]
#递归,向决策树字典添加分类信息,返回的结果就是一个字典
myTree[bestFeatLabel][value]=createTree(splitDataSet(dataSet,bestFeat,value), subLabels)
#返回决策树
return myTree
这里需要注意的是递归函数一定要有出口。
04.程序运行结果
创建一个测试函数:
#测试主函数
def main():
#数据集说明:yes为属于鱼类,no不属于鱼类
dataSet=[[1,1,'yes'],[1,0,'yes'],[1,0,'no'],[0,1,'no'],[0,1,'no']]
#对应特征名
labels=['no surfacing','flippers']
result=splitDataSet(dataSet, 0, 1)
result2=chooseBestFeatureToSplit(dataSet)
#生成树
result3=createTree(dataSet, labels)
print("计算数据集的熵:"+str(calcShannonEnt(dataSet)))
print("根据分离出单独数据集:"+str(result))
print("数据集最佳根:"+str(result2))
print("数据集最佳根:"+str(result3))
if __name__ == '__main__':
main()
程序运行结果:
{'flippers': {0: 'yes', 1: 'yes'}}
{'no surfacing': {0: 'no', 1: {'flippers': {0: 'yes', 1: 'yes'}}}}
计算数据集的熵:0.9709505944546686
根据分离出单独数据集:[[1, 'yes'], [0, 'yes'], [0, 'no']]
数据集最佳根:0
数据集最佳根:{'no surfacing': {0: 'no', 1: {'flippers': {0: 'yes', 1: 'yes'}}}}
[Finished in 0.3s]
写在后面
如果对机器学习,深度学习,自然语言处理等感兴趣的,可以关注博主的个人订阅号: