您的位置:首页 > 其它

机器学习实战_决策树

2017-03-24 16:46 274 查看
# -*- encoding:utf-8 -*-
from math import log
import operator
import matplotlib as plt

def calcShannonEnt(dataSet):
'''
1.首先计算数据集种实例的个数
2.然后创建一个词典,键值是最后一列的数值,如果键值不存在,那么扩展字典保存当前键值,每个键值记录当前类别出现的次数
3.最后,使用所在类别的发生频率计算类别出现的频率
我们将用这个概率计算香浓熵,统计所有类标发生的次数
熵越高,则混合的数据越多,可以在数据集中添加更多的分类,
例如观测熵的变化
:param dataSet:
:return:
'''
numEntries=len(dataSet)
labelsCounts={}
# 遍历数据记录
for fect in dataSet:
# 获得当前类标签
currentLabels=fect[-1]
if currentLabels not in labelsCounts.keys(): # 如果在标签-个数的字典中不存在,那么将添加一个值
labelsCounts[currentLabels]=0 # 添加一个标签字典
labelsCounts[currentLabels]+=1 # 统计+1
shannonEnt=0.0
# 获得类别(遍历所有类别)
for key in labelsCounts:
# 计算各个类别的频率
# p=label-count/total dataset
prob=float(labelsCounts[key])/numEntries
# IG-=(i to n)log2P(xi)
shannonEnt-=prob*log(prob,2)
return shannonEnt

def createDatsSet():
dataset=[
[1,1,'yes'],
[1,1,'yes'],
[1,0,'no'],
[0,1,'no'],
[0,1,'no']
]
labels=['no surfacing','flippers']
return dataset,labels
def splitDataSet(dataSet,axis,value):
'''
1.输入参数:待划分的数据集;划分数据集的特征;需要返回的特征值
2.新建一个list列表,遍历数据集中的每个元素,发现符合的元素则将其添加到新建的列表中

:param dataSet:数据集
:param axis: 拆分的属性特征(认为是最好的特征)
:param value:需要返回的特征值
:return:
'''
retDataSet=[] # 创建空的列表保存计算后的值
for fect in dataSet: # 遍历数据集
# 如果取出的特征是最好(需要返回的值)
# 那么将重组数据,提取出特征值,然后对剩下的数据进行重组,(特征值后面的值前移)
if fect[axis]==value:
reduceFectVec=fect[:axis] # 每条记录的前axis个值
reduceFectVec.extend(fect[axis+1:])
# 重组数据
retDataSet.append(reduceFectVec)
return retDataSet

<
4000
span style="color:#000080;font-weight:bold;">def chooseBestFeatureToSplit(dataSet):
'''
选择最好的数据集划分形式
1.首先获得数据记录的特征个数
2.计算数据集的熵
3.遍历每个特征i:
将数据集中的所有第i个特征进行抽取,然后唯一化
对每个唯一化的值value进行遍历:
数据子集=拆分数据集(数据集,第i个特征,唯一化遍历的值value)
prob=计算子集长度/数据集的长度
对唯一值的熵进行求和
4.比较所有特征中的信息增益,返回最好特征划分的索引值
:param dataSet:
:return:
'''
numFeatures=len(dataSet[0])-1 # 数据集的特征长度,这里需要为后面遍历每个特征提供数据
baseEntropy=calcShannonEnt(dataSet) # 计算熵(数据集)
beatInfoGain=0.0
bestFeature=-1

for i in range(numFeatures): # 遍历每个特征进行数据拆分
# 取出所有数据记录的第i个特征,组装成填充到列表featList
featList=[example[i] for example in dataSet]
# 对提取到的特征值进行唯一化
uniqueVals=set(featList)
newEntropy=0.0
for value in uniqueVals: # 遍历每个唯一化后的值
# 对第i个特征的值进行拆分出的数据子集
# 1.遍历当前特征中的所有唯一属性值,对每个特征划分一次数据集
# 2.然后计算数据集的新熵值,并对所有唯一特征值得到的熵求和,信息增益是熵的减少或数据无序度的减少
# 3.比较所有特征中的信息增益,返回最好特征划分的索引值
subDataSet =splitDataSet(dataSet,i,value)
prob=len(subDataSet)/float(len(dataSet))
newEntropy+=prob*calcShannonEnt(subDataSet)
infoGain=baseEntropy-newEntropy
if (infoGain > beatInfoGain):
beatInfoGain=infoGain
bestFeature=i
return bestFeature

# 原理:
# 1.得到原始数据集
# 2.然后基于最好的属性值划分数据集,由于特征值可能多于2个,
# 因此,可能存在大于两个分支的数据集划分
# (1)第一次划分后,数据将被向下传递到树分支的下一个节点
# (2)然后在这个节点上,我们可以再次划分数据
# 3.
# 递归的终止条件是:程序遍历完所有划分数据集的属性;
# 或者每个分支上的所有实例都具有相同的类
# 如果所有实例具有相同的分类,则得到一个叶子节点或终止块
# 任何达到叶子节点的数据必然属于叶子节点的分类
def majorityCnt(classList):
'''
多数表决的方法决定该叶子节点的分类
同时,查看是不是已经使用所有的属性(在还没找出最好的分类结论之前)
:param classList: 数据集的类别标签
:return:
'''
classCount={}
# 对类别数据集进行遍历
for vote in classList:
# 如果类别不在统计字典中,则添加一个
if vote not in classCount.keys():
# 计为0
classCount[vote]=0
# 类别+1
classCount[vote]+=1
#  对类别进行排序,
print classCount,'不排序前'
sortedClassCount=sorted(classCount.iteritems(),key=operator.itemgetter(1),reverse=True)
print sortedClassCount,'排序后'
return sortedClassCount[0][0]

def createTree(dataSet,labels):
'''
1. 输入两个参数:数据集和标签列表。
标签列表包含了数据集中所有特征的标签
算法本身并不需要这个变量,但是为了给出数据明确的含义,我们将它作为一个输人参数提供。
此外,前面提到的对数据集的要求这里依然需要满足。上述代码首先创建了名为。133^ 1 化的列表变量,
其中包含了数据集的所有类标签。递归函数的第一个停止条件是所有的类标签完全相同,则直接返回该类标签0 。
递归函数的第二个停止条件是使用完了所有特征,仍然不能将数据集划分成仅包含唯一类别的分组©。
由于第二个条件无法简单地返回唯一的类标签,这里使用程序清单3-3的函数挑选出现次数最多的类别作为返回值。
2.下一步程序开始创建树,这里使用Python语言的字典类型存储树的信息,当然也可以声明特
殊的数据类型存储树,但是这里完全没有必要。字典变量bestFeat存储了树的所有信息,这对于
其后绘制树形图非常重要。当前数据集选取的最好特征存储在变量beStFeat 中,得到列表包含
的所有属性值© 。这部分代码与程序清单3-3中的部分代码类似,这里就不再进一步解释了。
最后代码遍历当前选择特征包含的所有属性值,在每个数据集划分上递归调用函数
createTree ( ) ,得到的返回值将被插人到字典变量myTree中,因此函数终止执行时,宇典中将
会嵌套很多代表叶子节点信息的字典数据。在解释这个嵌套数据之前,我们先看一下循环的第一行
subLabels = labels[:],这行代码复制了类标签,并将其存储在新列表变量即证让訂沖。之
所以这样做,是因为在Python语言中函数参数是列表类型时,参数是按照引用方式传递的。为了保
证每次调用函数createTree时不改变原始列表的内容,使用新变量subLabels代替原始列表。
现在我们可以测试上面代码的实际输出结果,首先将程序清单3-4的内容输人到文件1^队?7
中’ 然后在Python命令提示符下输入下列命令:
:param dataSet:
:param labels:
:return:
'''
classList=[example[-1] for example in dataSet] # 获得所有标签
if classList.count(classList[0])==len(classList): #如果标签值的个数和数据长度一致,那么返回一个值就可以了
return classList[0]
if len(dataSet[0])==1: # 如果数据长度为1,(只有一个值)
return majorityCnt(classList)
bestFeat=chooseBestFeatureToSplit(dataSet) # 选择最好的特征
bestFeatLabels=labels[bestFeat] #取出最好标签的
# mytree={'flipper:{0,'no',1,'yes'}'}
myTree={bestFeatLabels:{}}
del(labels[bestFeat])
featValues=[example[bestFeat] for example in dataSet]
uniqueVals=set(featValues)
for value in uniqueVals:
subLabels=labels[:]
myTree[bestFeatLabels][value]=createTree(splitDataSet(dataSet,bestFeat,value),subLabels)
return myTree
def storeTree(inputTree,filename):
import pickle
fw=open(filename,'w')
pickle.dump(inputTree,fw)
fw.close()
def grabTree(filename):
import pickle
fr=open(filename,'r')
return pickle.load(fr)
class Mat:
def __init__(self):
self.de
a040
cisionNode=dict(boxstyle='sawtooth',fc='0.8')
self.leafNode=dict(boxstyle='round4',fc='0.8')
self.arrow_args=dict(arrowstyle='<-')
def createPlot(self):
fig=plt.figure(1,facecolor='white')
fig.clf()
plt.ax1=plt.subplot(111,frameon=False)

if __name__=='__main__':
myDat,lables=createDatsSet()
# 观察熵的变化
# myDat[0][-1]='maybe'
bestFeature=chooseBestFeatureToSplit(myDat)

# bestFeature=i 表示第i个特征是最好的用于划分数据集的特

print(myDat)
myTree=createTree(myDat,labels=lables)

# 变量myTree包含了很多代表树结构信息的嵌套字典,从左边开始,
# 第一个关键字 no surfacing 是第一个划分数据集的特征名称,该关键字的值也是另一个数据字典。
# 第二个关键字是 no surfacing 特征划分的数据集,这些关键字的值是no surfacing 节点的子节点。
# 这些值可能是类标签,也可能是另一个数据字典。
# 如果值是类标签,则该子节点是叶子节点;
# 如果值是另一个数据字典,则子节点是一个判断节点,这种格式结构不断重复就构成了整棵树。
# 本节的例子中,这棵树包含了3个叶子节点以及2个判断节点。
# {'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}

print myTree
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息