'''
决策树实例,决策树优点:
优点:计算复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可以处理不相关特征数据
缺点:可能会产生过度匹配问题
适用数据类型:数值型和标称型
'''
'''
决策树一般流程:
1、收集数据
2、准备数据
3、分析数据
4、训练数据
5、测试数据
6、使用算法
'''
from numpy import *
import operator
from os import listdir
from math import log
#创建数据集,标签集
def creatDataSet():
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 eachLine in dataSet: #遍历dataSet的每一行
currentLabel = eachLine[-1] #提取样本集中每个样本的标签,每一行数据的最后一个为标签
if currentLabel not in labelCounts.keys(): #labelCounts.keys()返回labelCounts所有键值
labelCounts[currentLabel] = 0 #如果当前样本的标签不在labelCounts中,将其值赋为0,
labelCounts[currentLabel] += 1 #如果在就加1,统计标签出现的次数
shannonEnt = 0.0
for key in labelCounts:
prob = float(labelCounts[key])/numEntries #计算p(x_i)
shannonEnt -= prob * log(prob,2) #计算熵值
return shannonEnt
#按照给定属性划分数据集
# dataSet: 需要划分的数据集
# axis: 划分数据集的属性 0: 不浮出水面是否可以生存 1: 是否有脚蹼
# value: 需要返回的特征的值 0: 否 1:是
def splitDataSet(dataSet,axis,value):
retDataSet = [] #创建新的列表对象,防止列表被修改
for featVec in dataSet: #dataSet中每个元素也为一个列表,遍历dataSet中每个元素
if featVec[axis] == value: #提取符合特征的数据
reducedFeatVec = featVec[:axis]
reducedFeatVec.extend(featVec[axis+1:])
retDataSet.append(reducedFeatVec)
return retDataSet
#选择最好的数据划分方式
def chooseBestFeatureToSplit(dataSet):
numFeatures = len(dataSet[0]) - 1 #dataSet第一行长度减去标签,即为属性个数
baseEntropy = calcShannonEnt(dataSet) #计算整个数据集的熵
bestInfoGain = 0.0; bestFeature = -1
for i in range(numFeatures): #遍历数据集中所有的特征
featList = [example[i] for example in dataSet] #取出dataSet中每个元素(dataSet中元素为列表)中的第i个元素组成一个列表
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
def majorityCnt(classList): #返回出现次数最多的分类名称
classCount = {}
for vote in classCount:
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 creatTree(dataSet,labels):
classList = [example[-1] for example in dataSet] #取出dataSet中每个元素(dataSet中元素为列表)中的最后一列元素组成一个列表,即为类别
if classList.count(classList[0]) == len(classList): #count()方法统计classList中classList[0]出现的次数,如果与长度相等,则表明全为一类
return classList[0]
if len(classList) == 1 :
return majorityCnt(classList)
bestFeat = chooseBestFeatureToSplit(dataSet)
bestFeatLabel = labels[bestFeat]
myTree = {bestFeatLabel:{}}
del(labels[bestFeat])
featValues = [example[bestFeat] for example in dataSet]
uniqueValues = set(featValues)
for value in uniqueValues:
subLabels = labels[:] #复制类标签
myTree[bestFeatLabel][value] = creatTree(splitDataSet(dataSet,bestFeat,value),subLabels) #递归创建决策树
return myTree