考完科目一啦,继续学习
抓紧时间,认真学习,提高效率
==================================================
简单的,计算香农信息熵的函数
from math import log
def calcShannonEnt(dataset):
numEntries=len(dataset)
labelCount={}
for featVec in dataset:
currentLabel=featVec[-1]
if currentLabel not in labelCount.keys():
labelCount[currentLabel]=0
#如果不存在先置为0
labelCount[currentLabel]+=1
#所有label对应的个数增加1
shannonEnt=0.0
for key in labelCount:
prob=float(labelCount[key])/numEntries
#按照个数计算比例
shannonEnt-=prob*log(prob,2)
#每轮熵叠加
#log函数以参数2为底,没有的话默认为e
return shannonEnt
辅助:创建简单的数据集用于测试
def createDataset():
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):
retDataSet=[]
for featVec in dataset:
if featVec[axis]==value:
reducedFeatVec=featVec[:axis]
#截取AXIS维前面的数据
reducedFeatVec.extend(featVec[axis+1:])
#拼接AXIS维后面的数据
retDataSet.append(reducedFeatVec)
#留心append和extend的区别
return retDataSet
其中比较重要的区别是书中提到的append和extend
书中讲使用新的列表是为了不破坏原列表
最简答的例子就是:
>>> a=[1,2,3]
>>> b=[4,5,6]
>>> a.extend(b)
>>> a
[1, 2, 3, 4, 5, 6]
>>> a.append(b)
>>> a
[1, 2, 3, 4, 5, 6, [4, 5, 6]]
extend拼接的是参数的内容,append是将参数当做一个元素
能计算信息熵,按照维度划分数据集后,就开始要选择最好的划分方式啦。
def chooseBestFeatureToSplit(dataset):
numFeature=len(dataset[0])-1
#特征个数的获取是第一行的长度-1,具体还是要看数据集的
baseEntropy=calcShannonEnt(dataset)
#计算划分前的信息熵
bestInfoGain=0.0
bestFeature=-1
#初始化最优信息增益和最优特征
for i in range(numFeature):
featList=[example[i] for example in dataset]
#相当于取dataset第i列的值
unique=set(featList)
#由set类型转为来求取不重复的值
newEntropy=0.0
#设置第i维划分后信息熵为00
for value in unique:
subDataset=splitDataset(dataset,i,value)
#针对第i维的每一种取值划分子数据集
prob=float(len(subDataset))/len(dataset)
newEntropy+=prob*calcShannonEnt(subDataset)
#第i维特征每一种取值的信息熵叠加
infoGain=baseEntropy-newEntropy
#第i维的信息增益
if(infoGain>bestInfoGain):
bestFeature=i
bestInfoGain=infoGain
#如果该维信息增益大于当前最大信息增益,则替换
return bestFeature
下面函数是以多数表决的形式定义类标签并不统一的叶子节点
<pre name="code" class="python">def majorityCnt(classlist):
classCount={}
#初始化类标签计数为空
for vote in classlist:
if vote not in classCount.keys():
#如果该标签不在字典内,则计为0
classCount[vote]=0
classCount[vote]+=1
#所有类标签计数加1
sortedClassCount=sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
#对类标签计数结果排序
#该sorted函数表示,classCount.items()获得classCount字典内的键对值列表。
#operator.itemgetter(1)定义获得item的第二个域(第一个是0)的值
return sortedClassCount[0][0]
#返回排序最高的类标签
使用递归来构建决策树:
递归的结束条件为划分完所有属性集或者每个分支下的实例都具有相同分类。
def createTree(dataSet,labels):
#此处有必要注释一下,labels表示的不是数据分类类别的标签,而是特征的标签
classlist=[example[-1] for example in dataSet]
#从dataSet最后一列中获取类标签列表
if classlist.count(classlist[0])==len(classlist):
return classlist[0]
#此处为递归终止的第一种情况:子集内所有类标签一样
#classlist以第一项元素的标签计数,如果等于列表个数,说明列表中值都是一样的
if len(dataSet[0])==1:
return majorityCnt(classlist)
#递归终止的另一情况:已经划分完所有属性特征
#len(dataSet[0])==1表示划分到最后只剩下一列类别标签
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[:]
#复制获得子维度标签,此时labels中已删除当前最优维度标签
myTree[bestFeatLabel][value]=createTree(splitDataset(dataSet,bestFeat,value),sublabels)
#递归对每个分支建立子树。使用到了之前的splitdataset划分子树函数
return myTree
=========================================================================================
补充:python内的字典类型字典是Python语言中唯一的映射类型。
映射类型对象里哈希值(键,key)和指向的对象(值,value)是一对多的的关系,通常被认为是可变的哈希表。
字典对象是可变的,它是一个容器类型,能存储任意个数的Python对象,其中也可包括其他容器类型。
重要特点:映射类型是无序的。
adict = {}
adict = {key1:value2, key2:value2, …}
特点:
1、键与值用冒号“:”分开;
2、项与项用逗号“,”分开;
3、字典中的键必须是唯一的,而值可以不唯一。
字典的基本操作:
1、如何访问字典中的值?
adict[key] 形式返回键key对应的值value,如果key不在字典中会引发一个KeyError。
2、如何检查key是否在字典中?
a、has_key()方法 形如:adict.haskey(‘name') 有–>True,无–>False
b、in 、not in 形如:'name' in adict 有–>True,无–>False
3、如何更新字典?
a、添加一个数据项(新元素)或键值对
adict[new_key] = value 形式添加一个项
b、更新一个数据项(元素)或键值对
adict[old_key] = new_value
c、删除一个数据项(元素)或键值对
del adict[key] 删除键key的项 / del adict 删除整个字典
adict.pop(key) 删除键key的项并返回key对应的 value值
字典的方法:
1、adict.keys() 返回一个包含字典所有KEY的列表;
2、adict.values() 返回一个包含字典所有value的列表;
3、adict.items() 返回一个包含所有(键,值)元祖的列表;
4、adict.clear() 删除字典中的所有项或元素;
5、adict.copy() 返回一个字典浅拷贝的副本;
6、adict.fromkeys(seq, val=None) 创建并返回一个新字典,以seq中的元素做该字典的键,val做该字典中所有键对应的初始值(默认为None);
7、adict.get(key, default = None) 返回字典中key对应的值,若key不存在字典中,则返回default的值(default默认为None);
8、adict.has_key(key) 如果key在字典中,返回True,否则返回False。 现在用 in 、 not in;
9、adict.iteritems()、adict.iterkeys()、adict.itervalues() 与它们对应的非迭代方法一样,不同的是它们返回一个迭代子,而不是一个列表;
10、adict.pop(key[,default]) 和get方法相似。如果字典中存在key,删除并返回key对应的vuale;如果key不存在,且没有给出default的值,则引发keyerror异常;
11、adict.setdefault(key, default=None) 和set()方法相似,但如果字典中不存在Key键,由 adict[key] = default 为它赋值;
12、adict.update(bdict) 将字典bdict的键值对添加到字典adict中。
==========================================================================
补充:sorted函数和operator.itemgetter函数
来源:点击打开链接
operator.itemgetter函数
operator模块提供的itemgetter函数用于获取对象的哪些维的数据,参数为一些序号(即需要获取的数据在对象中的序号),下面看例子。
a = [1,2,3]
>>> b=operator.itemgetter(1) //定义函数b,获取对象的第1个域的值%此处可以看出定义的是函数
>>> b(a)
2
>>> b=operator.itemgetter(1,0) //定义函数b,获取对象的第1个域和第0个的值
>>> b(a)
(2, 1)
要注意,operator.itemgetter函数获取的不是值,而是定义了一个函数,通过该函数作用到对象上才能获取值。
sorted函数Python内置的排序函数sorted可以对list或者iterator进行排序,该函数原型为:sorted(iterable[, cmp[, key[, reverse]]])
参数解释:
(1)iterable指定要排序的list或者iterable,不用多说;
(2)cmp为函数,指定排序时进行比较的函数,可以指定一个函数或者lambda函数,如:
students为类对象的list,没个成员有三个域,用sorted进行比较时可以自己定cmp函数,例如这里要通过比较第三个数据成员来排序,代码可以这样写:
students = [('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]
sorted(students, key=lambda student : student[2])
(3)key为函数,指定取待排序元素的哪一项进行排序,函数用上面的例子来说明,代码如下:
sorted(students, key=lambda student : student[2])
key指定的lambda函数功能是去元素student的第三个域(即:student[2]),因此sorted排序时,会以students所有元素的第三个域来进行排序。
有了上面的operator.itemgetter函数,也可以用该函数来实现,例如要通过student的第三个域排序,可以这么写:
sorted(students, key=operator.itemgetter(2))
sorted函数也可以进行多级排序,例如要根据第二个域和第三个域进行排序,可以这么写:
sorted(students, key=operator.itemgetter(1,2))
即先跟句第二个域排序,再根据第三个域排序。
(4)reverse参数就不用多说了,是一个bool变量,表示升序还是降序排列,默认为false(升序排列),定义为True时将按降序排列。
使用递归来构建决策树:
递归的结束条件为划分完所有属性集或者每个分支下的实例都具有相同分类。