Python实现——决策树实例(离散数据/香农熵)
决策树的实现太...繁琐了。
如果只是接受他的原理的话还好说,但是要想用代码去实现比较糟心,目前运用了《机器学习实战》的代码手打了一遍,决定在这里一点点摸索一下该工程。
实例的代码在使用上运用了香农熵,并且都是来处理离散数据的,因此有一些局限性,但是对其进行深层次的解析有利于对于代码的运作,python语言的特点及书写肯定是有帮助的。
我们分别从每个函数开始:
- 计算香农熵
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
该函数为当前的数据集计算香农熵。
其中,numEntries用来计数数据数目
numEntries = len(dataSet)
其后,该函数运用了一个字典来计算各个最终类(即我们所要最终分开的特点的所有类型,比如Titanic题目中就是是否生还,Coursera课程中的例子就是这个贷款是否安全)的出现数目,其中,该最终数据是处在数据集的最后一列的,因此运用
currentLabel = featVec[-1]
让currentLabel暂时记住当前数据的最终类型,倘使该类型不存在,就要用
if currentLabel not in labelCounts.keys():
labelCounts[currentLabel] = 0
将其插入字典并将它的键值初始化为0(即出现0次),最后用
labelCounts[currentLabel] +=1
计数代表当前最终类型出现数目+1
之后便是对于香农熵的计算,这里的代码上的理解并不困难
for key in labelCounts:
prob = float(labelCounts[key])/numEntries
shannonEnt -= prob * log(prob,2)
log(prob,2)是以2为底数求对数
- 划分数据集
def splitDataSet(dataset, axis, value):
retDataSet = []
for featVec in dataset:
if featVec[axis] == value:
reducedFeatVec = featVec[:axis]
reducedFeatVec.extend(featVec[axis+1:])
retDataSet.append(reducedFeatVec)
return retDataSet
该函数使用了三个参数:待划分的数据集,划分数据集的特征(即第axis个属性),需要返回的特征的值(该属性的取值value)。
首先由于需要将一类数据放于一起,但是python在函数中传递的是列表的引用,直接修改也会在全局上对列表产生变化,为了避免这种影响就需要新建一个空表存储目标数据。
retDataSet = []
再之后就是要将相应的数据放入这个列表中。
其中为了更好的进行以下的操作,因为本工程控制划分数目的方式看来是限定在一条线路中每个属性最多有一次作为划分指标,因此需要将其在加入retDataSet时去除,由下列代码实现:
reducedFeatVec = featVec[:axis]
reducedFeatVec.extend(featVec[axis+1:])
retDataSet.append(reducedFeatVec)
书中提到了append()与extend()函数的区别,前者可以将列表整体作为一个元素加入新列表,后者则是将列表的元素分别加入新列表。
- 选出最好的划分方式
def chooseBestFeatureToSplit(dataset):
numFeatures = len(dataset[0]) - 1
baseEntropy = calcShannonEnt(dataset)
bestInfoGain = 0.0
bestFeature = -1
for i in range(numFeatures):
featList = [example[i] for example in dataset]
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
当下我们所要做的是要遍历当前的数据集,不断计算所有属性划分对应的香农熵,由此选出最适合作为当前状况下划分标准的属性。
为了防止最终属性被选择,我们需要将其排除在外,默认最终属性一般会出现在最后一列,
numFeatures = len(dataset[0]) - 1
之后在for循环中numFeatures数目便不会再囊括最终属性了。
在这个时候我纠结了一下,万一最好的划分就是最终属性呢?不过目前我已经说服自己了,毕竟最终属性是我们的目标,不管怎样我们都要避开它并使用别的属性来进行划分。
之后初始化变量,分别来存储新的香农熵变化和当前数据。
bestInfoGain = 0.0
bestFeature = -1
现在进入这个遍历所有属性的for大循环。一开始我们先将当前属性的所有可能值传入一个空集合(即无重复元素的集合)。
featList = [example[i] for example in dataset]
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
其中用splitDataSet(),将当前处理的属性作为划分依据,用其内部所有可能的取值来分出子集,并计算对应的香农熵,然后根据比例再加权求和,得到该属性划分下的香农熵newEntropy以及相比直接作为叶子节点得到的香农熵的差infoGain。
if(infoGain>bestInfoGain):
bestInfoGain = infoGain
bestFeature = i
最后与当前记录的最优属性的结果比对,如果更好就覆盖。最后返回最优属性。
- 选取出现最多的属性
def majorityCnt(classList):
classCount={}
for vote in classList:
if vote not in classCount.keys():classCount[vote]=0
classCount[vote] += 1
sortedClassCount = sorted(classClount.iteritems(),\
key=operator.itemgetter(1),reverse=True)
return sortedClassCount[0][0]
如果当前划分时已经没有更多的属性了,那么该节点自动变为叶子节点,其分类由其数据中最终属性出现最多的分类决定,即根据属性的分类创建一个字典classCount,并用分类作为键,出现次数为对应值。
随后用operator库中的sort()函数为其排序,选出出现最多的最终属性,以此作为该叶子节点的分类。
- 主体:创建树
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]
uniqueVals = set(featValues)
for value in uniqueVals:
subLabels = labels[:]
myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet,bestFeat,value),subLabels)
return myTree
这个函数运用了递归的想法,最终结果是以字典中的字典形式来表示的,虽然并不是特别直观,但至少是我能接受的工程量。
首先进行了两个判断,一是若当前数据集在最终分类上已经达成了共识,那么就不需要在进行分类了:
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])
紧接着要为上面刚选定的属性分类划分出各个子节点,并为其递归再次生成子树,方法集合了上文的许多技巧,暂时不再赘述。
以上,是我对于《机器学习实战》中的决策树实现代码的解析。
Python实现——决策树实例(离散数据/香农熵)的更多相关文章
- Python实现——决策树(部分函数/连续数据)
由于上一例的实现中只针对了离散数据,为了扩充处理范围,我实现了一下对线性数据的简单处理,在其中我选择用中位数作为指标,平均数.众数等等其他数据在我看来异曲同工,最终也都会有较相似的结构. 求连续数据的 ...
- python第六天 函数 python标准库实例大全
今天学习第一模块的最后一课课程--函数: python的第一个函数: 1 def func1(): 2 print('第一个函数') 3 return 0 4 func1() 1 同时返回多种类型时, ...
- python对离散数据进行编码
机器学习中会遇到一些离散型数据,无法带入模型进行训练,所以要对其进行编码,常用的编码方式有两种: 1.特征不具备大小意义的直接独热编码(one-hot encoding) 2.特征有大小意义的采用映射 ...
- python 类和实例
面向对象最重要的概念就是类(Class)和实例(Instance),必须牢记类是抽象的模板,比如Student类,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可 ...
- python使用xlrd读取excel数据时,整数变小数的解决办法
python使用xlrd读取excel数据时,整数变小数: 解决方法: 1.有个比较简单的就是在数字和日期的单元格内容前加上一个英文的逗号即可.如果数据比较多,也可以批量加英文逗号的前缀(网上都有方法 ...
- Python Socket请求网站获取数据
Python Socket请求网站获取数据 ---阻塞 I/O ->收快递,快递如果不到,就干不了其他的活 ---非阻塞I/0 ->收快递,不断的去问,有没有送到,有没有送到,. ...
- python操作txt文件中数据教程[4]-python去掉txt文件行尾换行
python操作txt文件中数据教程[4]-python去掉txt文件行尾换行 觉得有用的话,欢迎一起讨论相互学习~Follow Me 参考文章 python操作txt文件中数据教程[1]-使用pyt ...
- Python操作Mysql实例代码教程在线版(查询手册)_python
实例1.取得MYSQL的版本 在windows环境下安装mysql模块用于python开发 MySQL-python Windows下EXE安装文件下载 复制代码 代码如下: # -*- coding ...
- python类和实例以及__call__/__del__
面向对象最重要的概念就是类(Class)和实例(Instance),必须牢记类是抽象的模板,比如Student类,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可 ...
随机推荐
- Python 约束 , 自定义异常 , 加密 , 日志
约束 约束 , 约束其派生类: 保证派生类中必须编写send方法 , 不然执行可能就会报错 Python中 语法: class BaseMessage(object): def send(self ...
- 微信开发准备(四)--nat123内网地址公网映射实现
转自:http://www.cuiyongzhi.com/post/37.html 在前面几篇中我们一直说的开发准备工作主要是基于开发框架上的,这里我们说的就逐渐接近开发过程中的实体操作了,如果你之前 ...
- **python中列表 元组 字典 集合
列表 元组 字典 集合的区别是python面试中最常见的一个问题.这个问题虽然很基础,但确实能反映出面试者的基础水平. 1.列表 列表是以方括号“[]”包围的数据集合,不同成员以“,”分隔. 列表的特 ...
- mysql之提示符
MySQL常用的简单的命令: mysql> PROMPT \u@\h \d> PROMPT set to '\u@\h \d>' root (none)>USE test Da ...
- T-SQL 理解SQL SERVER中的分区表(转)
转载来源一定要明显: http://www.cnblogs.com/CareySon/archive/2011/12/30/2307766.html 而且这个大神对于数据库方面的文章非常棒 强烈推荐 ...
- java基础之多线程三:多线程并发同步
由于线程的执行是CPU随机调度的,比如我们开启10个线程,这10个线程并不是同时执行的,而是CPU快速的在这10个线程之间切换执行,由于切换速度极快使我们感觉同时执行罢了. 线程同步问题往往发生在多个 ...
- 在cmd中 操作 数据库 MySQL 的一些命令
环境变量配置配置好以后, 打开cmd 连接:mysql -h主机地址 -u用户名 -p用户密码 (注:u与root可以不用加空格,其它也一样) 断开:exit (回车) 创建授权:grant sele ...
- Luogu 4755 Beautiful Pair
分治 + 主席树. 设$solve(l, r)$表示当前处理到$[l, r]$区间的情况,我们可以找到$[l, r]$中最大的一个数的位置$mid$,然后扫一半区间计算一下这个区间的答案. 注意,这时 ...
- flex 布局的深入研究
对于flex盒模型的设计期望 flex盒模型是被期望设计成 1:在任何流动的方向上(包括上下左右)都能进行良好的布局 2:可以以逆序 或者 以任意顺序排列布局 3:可以线性的沿着主轴一字排开 或者 沿 ...
- Asp.NET中把DataTable导出为Excel ,中文有乱码现象解决办法
//DataTable为要导出的数据表 DataGrid dg = new DataGrid(); dg.DataSource = DataTable; ...