参考数据:
创建决策树的代码实现:
from math import log
import operator
'''
(1)函数说明:创建测试数据集
返回值:dataSet:数据集
labels:分类属性
'''
def createDataSet():
# 数据集.
'''
1列:年龄:0代表青年,1代表中年,2代表老年;
2列:有工作:0代表否,1代表是;
3列:有自己的房子:0代表否,1代表是;
4列:信贷情况:0代表一般,1代表好,2代表非常好;
5列:类别(是否给贷款):no代表否,yes代表是。(结果)
'''
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']]
#分类的属性(4个特征)
labels = ['年龄','有工作','有自己的房子','信贷情况']
return dataSet,labels #返回数据集、分类属性
'''
(2)函数说明:计算数据集dataSet的熵
其公式为:H(D)= -p(x1)log2p(x1)-p(x2)log2p(x2)-....
返回值:该数据集dataSet的熵
'''
def calcShannonEnt(dataSet):
#获取数据集的行数
numEntries = len(dataSet)
#创建字典。键:分类的结果 值:每个结果出现的次数
labelCounts = {}
#遍历数据集,统计每个结果出现的次数
for featVec in dataSet:
#最后一列元素存储的是分类结果
currentLabel = featVec[-1]
#如果当前分类的结果不在labelCounts中,则在labelCounts中加入该键
if currentLabel not in labelCounts.keys():
labelCounts[currentLabel] = 0
#将当前分类的值+1
labelCounts[currentLabel]+=1
#用来存储熵的结果
shannonEnt = 0
#开始计算熵.遍历字典labelCounts
for key in labelCounts:
#计算该结果出现的频率
prob = float(labelCounts[key])/numEntries
#计算熵
shannonEnt -=prob*log(prob,2)
#返回熵
return shannonEnt
'''
(3)函数说明:按照给定的特征划分数据集
抽取数据集dataSet中 axis列中 值为value的部分数据集
特 征,结果
例如: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']]
axis = 0
value = 0
找第0列,值为0的行(即前5行),并除去该列(即第一列)。将结果(前5行后4列)存放在retDataSet中
retDataSet = [[0,0,0,'no'],
[0,0,1,'no'],
[1,0,1,'yes'],
[1,1,0,'yes'],
[0,0,0,'no']]
参数:dataSet:数据集
axis:表示列下标
value:表axis列(特征)的取值
该函数的目的;帮助计算Di的样本个数(行数)|Di|—> 计算|Di|/|D|、H(Di)—> 计算H(D|A)
根据特征A(axis列的列名)的取值,将D划分为n个子集D1,D2,....,Dn
'''
def splitDataSet(dataSet,axis,value):
# 用来存储符合条件的数据集
retDataSet = []
#遍历数据集dataSet(一次取一行)
for featVec in dataSet:
#如果axis列的值为value,将取出的这行数据(不包含axis列)存放在retDataSet中
if featVec[axis] == value: #不去除axis列也可以
#取该行下标0~axis的元素(不包含axis)
reduceFeatVec = featVec[:axis]
#接着取下标axis+1往后的所有元素(不包含axis)
reduceFeatVec.extend(featVec[axis+1:])
#将去除axis列的该行元素存放在retDataSet中
retDataSet.append(reduceFeatVec)
return retDataSet
'''
(4)函数说明:选择最好的数据集划分方式。
(即选择对结果影响最大的特征。特征A的信息增益越大,影响越大)
分别计算所有特征下D的信息增益g(D,A)=H(D)-H(D|A),取最大的信息增益所对应的特征(H(D)表示数据集D的熵)
参数dataSet:数据集
返回值bestFeature:最大的信息增益对应的特征下标
'''
def chooseBestFeatureToSplit(dataSet):
#获取特征总数.dataSet[0]表示总列数
numFeatures = len(dataSet[0])-1
#计算数据集D的熵
baseEntropy = calcShannonEnt(dataSet)
#存最大的信息增益
bestInfoGain = 0.0
#存最大信息增益对应的特征
bestFeature = -1
#遍历所有的特征,获取最大的信息增益及其对应的特征
for i in range(numFeatures):
#获取数据集中第i列的所有取值
featList = [example[i] for example in dataSet]
#去掉featList中重复的取值。set集合元素不可重复
uniquevals = set(featList)
#newEntropy存放特征i下的熵
newEntropy = 0.0
#遍历特征i下的各种取值,计算特征i下的熵(需要计算出|Di|/|D|,即单独计算出特征i下各种取值的熵)
for value in uniquevals:
#subDataSet:特征i取值为value时的部分数据集
subDataSet = splitDataSet(dataSet,i,value)
#计算部分数据集所占的概率
prob = len(subDataSet)/float(len(dataSet))
#计算熵
newEntropy += prob*calcShannonEnt(subDataSet)
#计算特征i的信息增益
infoGain = baseEntropy-newEntropy
print("第%d特征的信息增益为:%.3f"%(i,infoGain))
#寻找最大的信息增益
if(infoGain>bestInfoGain):
#最大的信息增益
bestInfoGain = infoGain
#最大信息增益对应的特征下标
bestFeature = i
return bestFeature
# if __name__ == '__main__':
# dataSet,labels = createDataSet()
# print("最大信息增益值:"+str(chooseBestFeatureToSplit(dataSet)))
'''
(5)函数说明;统计结果列表classList中出现次数最多的结果
参数说明:classList 结果列表
返回值:出现次数最多的结果
'''
def majorityCnt(classList):
#创建字典(键:结果、值:该结果出现的次数),用来计每个结果出现的次数。
classCount = {}
#遍历结果集classList
for key in classList:
#如果该结果不在字典中,就将其添加到字典中
if key not in classCount.keys():
classCount[key] = 0
#将该结果的次数加1
classCount[key]+=1
#对classCount字典按值降序排列
sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
#返回出现次数最多的结果
return sortedClassCount[0][0]
'''
(6)函数说明:创建树
参数: dataSet:数据集
labels:特征集
返回值:创建的树
'''
def createTree(dataSet,labels):
#获取最后一列的元素(所有的结果)
classList = [example[-1] for example in dataSet]
#当划分结果完全一样时,停止划分,跳出循环
if classList.count(classList[0]) == len(classList):
#返回划分的结果
return classList[0]
#如果所有的特征遍历完时(因为是递归,每次都会把参数数据集dataSet划分,划分到最后,只剩下最后一列)
if len(dataSet[0]) == 1:
#返回出现次数最多的结果
return majorityCnt(classList)
#获取信息增益最大的特征下标(即影响最大的特征下标)
bestFeat = chooseBestFeatureToSplit(dataSet)
#获取信息增益最大的特征
bestFeatLabel = labels[bestFeat]
#创建根节点为bestFeatLabel的树(字典)
myTree = {bestFeatLabel:{}}
#在labels特征集中删除该特征
del(labels[bestFeat])
#得到下标为bestFeat列(即信息增益最大的特征)的所有取值
featValues = [example[bestFeat] for example in dataSet]
#除去重复值
uniqueVals = set(featValues)
#遍历该特征的所有取值,为每个取值创建决策树
for value in uniqueVals:
#获取当前所有的特征(不包含已经创建树的特征)
subLabels = labels[:]
#splitDataSet(dataSet,bestFeat,value):将当前数据集进行划分(获取下标为bestFeat特征,值为value的子数据集)
#subLabels:当前所有的特征(不包含已经创建树的特征)
myTree[bestFeatLabel][value]=createTree(splitDataSet(dataSet,bestFeat,value),subLabels)
return myTree
# if __name__ =='__main__':
# dataSet,labels = createDataSet()
# myTree = createTree(dataSet,labels)
# print(myTree)
实现结果: