喜欢可以关注【小猪课堂】公众号了解更多内容
from math import log
import pandas as pd
import numpy as np
数据集
def createDataSet():
dataSet=[[1,1,'yes'],
[1,1,'yes'],
[1,0,'no'],
[0,1,'no'],
[0,1,'no']]
labels=['no surfacing','flippers','target']
return dataSet ,labels
信息熵
def calcShannonEnt(dataSet):#注意使用这个函数时尽量是用numpy的形式不要直接将DataFrame直接写进去
numEntries=len(dataSet)#数据集个数
labelCounts={}#这是个字典,千万别认为是集合(以后后面会只用到key
for featVec in dataSet: #其实我没有明白这个循环的意思,要是根据下面那个句代码来推断其实就是遍历说有的列
currentLabel=featVec[-1]#选择target
if currentLabel not in labelCounts.keys():#统计target中的个数
labelCounts[currentLabel]=0#字典中 dict[key]=value
labelCounts[currentLabel]+=1#字典中value的加法
shannonEnt =0.0#定义熵为0 而且是float
for key in labelCounts:#遍历所以的key
prob = float(labelCounts[key])/numEntries#prob(概率)
shannonEnt-=prob*log(prob,2)#这是使用统计学的熵函数
# print(labelCounts)
return shannonEnt#返回值可是供调用时用
划分数据集
# 如果是第一次看这个你可能有点蒙,但是看了多次后你会发现他只不过是一个非常简单。
#一列数据可能存在很多特征(类),然后将这这个特征选择出符合这个特征的数据子集
def splitDataSet(dataSet,axis,value):
#数据集 :不解释
#axis:就是在这个轴上(默认 axis=为列),我有点分不清axis=0/1
#value:数值,其实这个值就跟这个轴上的类别差不多,也不是差不多就是一回事
#将在数据集中某个轴上数值=x的数据筛选出来并保存为子集retDataSet
retDataSet=[]#子集
for featVec in dataSet:#遍历所有的列,包括target
if featVec[axis] ==value:#如果这个列中的某一个值等于我们想要的目标值
reduceFeatVac=featVec[:axis]#[0,1,2,3,4,x,6,7,8,n] [:x)=[0,1,2,3,4]
reduceFeatVac.extend(featVec[axis+1:])#[x=1:]=[6,7,8,n] 所以reducefeatvac中单独缺少x这个值[0,1,2,3,4,6,7,8,n]
#存在一个问题就是'numpy.ndarray' object has no attribute 'extend'
retDataSet.append(reduceFeatVac)#[[0,1,2,3,4,6,7,8,n],educefeatvac2,educefeatvac3,,]
return retDataSet#返回子集列表
如何选择最好的划分方式
def chooseBestFeatureToSplit(dataSet):
numFeatures=len(dataSet[0])-1#在理想的数据集中一般会把最后一列作为target,所以features的个数就是len(dataSet)个
baseEntropy=calcShannonEnt(dataSet)#香农熵calcshannonent是一个我们定义的函数
bestInfoGain=0.0#首先定义一个信息增益为0并且是float的数据类型
bestFeature=-1#最好的特征为-1,这也包含了特殊值的处理
for i in range(numFeatures):#遍历所有feature
featList =[example[i] for example in dataSet]#产生某一特征的列表,不理解可以看下面的例子,理解可以略过
#[example for example in mydat]
#>>[[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]
#[example[1] for example in mydat]
#>>[1, 1, 0, 1, 1]
uniqueVals=set(featList)#将featList列表变成集合,集合特征是将无序不重复,所以也就意味着已经将featList中变成没有冲服相的特征
newEntropy=0.0#新的信息增益,并且float数据类型
for value in uniqueVals:#变量特征中的类别,为求条件熵做数据准备
subDataSet=splitDataSet(dataSet,i,value)#部分数据集,就是我上面跟大家讲的retdataset,叫条件子集比较好
prob=len(subDataSet)/float(len(dataSet))#这个是权重比例就是\Dv\/\D\,不懂去看条件熵公式,不懂公式别着急看算法代码,这样很吃力
newEntropy+=prob*calcShannonEnt(subDataSet)#这个好理解
infoGain=baseEntropy-newEntropy#信息增益=信息熵-sum(条件熵)
if (infoGain>beatInfogain):#信息增益的选择,选择出信息增益值醉大的分类作为最好的划分,增加数据纯度
bestInfoGain=infoGain#最好的信息增益
bestFeature=i#最好的划分属性
return bestFeature#输出最好属性所在列的角标
创建树的函数代码
def majorityCnt(classList):
classCount={}#类别汇总
for vote in classList:#
if vote not in classCount.keys(): classCount[vote] = 0#一、这种写法第一次见,二、这个逻辑非常聪明,将不存在的类别添加后赋值数量为0
classCount[vote] += 1#在if循环外进行叠加
sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)#降序
#sorted(数据集,依据,排序)
return sortedClassCount[0][0]#返回首列最大值
def createTree(dataSet,labels):#数据集和标签
classList = [example[-1] for example in dataSet]#生成target结果list,这个地方能不能使用dataframe目前还不知道
#[example for example in mydat]
#>>[[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]
#[example[1] for example in mydat]
#>>[1, 1, 0, 1, 1]
if classList.count(classList[0]) == len(classList):#最终含义就是,如果结果唯一怎么样
#这个count()函数地方提现了这个作者代码功底的扎实,df.count()括号内没有内容是报错的,如果括号内有内容要书写统计的内容,
#比如‘yes’就会返回在classlist中yes的个数
return classList[0]#stop splitting when all of the classes are equal,返回类列表
if len(dataSet[0]) == 1: #stop splitting when there are no more features in dataSet,如果数据集中只有target数据没有变量
return majorityCnt(classList)
bestFeat = chooseBestFeatureToSplit(dataSet)#最好的分类列
bestFeatLabel = labels[bestFeat]#最好的特征名称
myTree = {bestFeatLabel:{}}#
del(labels[bestFeat])#从label中删除最好的特征值
featValues = [example[bestFeat] for example in dataSet]#选择出最好的特征的list
uniqueVals = set(featValues)#计算出最好特征下分存在多少类
for value in uniqueVals:#遍历这些类后
subLabels = labels[:]#copy all of labels, so trees don't mess up existing labels,相当于复制生成部分labels
myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels)#循环直到产生两个if条件为真
return myTree#返回整个tree
使用matplotlib绘图
import matplotlib.pyplot as plt
decisionNode = dict(boxstyle="sawtooth", fc="0.8")#决策节点=字典(方框类型=锯齿型,fc是边框粗细)
leafNode = dict(boxstyle="round4", fc="0.8")#叶子节点=字典(方框类型=圆,)
arrow_args = dict(arrowstyle="<-")#arrow_args为箭头=字典(箭头类型arrowstyle=‘<-’)
def plotNode(nodeTxt, centerPt, parentPt, nodeType):
#定义结点图(nodeTxt用于记录nodeTxt,即节点的文本信息。centerPt表示那个节点框的位置。 parentPt表示那个箭头的起始位置。
#nodeType表示的是节点的类型,也就会用我们之前定义的全局变量)
createPlot.ax1.annotate(nodeTxt, xy=parentPt, xycoords='axes fraction',xytext=centerPt, textcoords='axes fraction',
va="center", ha="center", bbox=nodeType, arrowprops=arrow_args )
#创建图片,ax1,annotate:注释((string)nodeTxt=注释文本, xy=注释位置, xycoords='axes fraction',xytext=注释文本位置,
#textcoords='axes fraction',va=垂直对齐方式, ha=水平对齐方式, bbox=nodeType, arrowprops=arrow_args)
def createPlot():
fig = plt.figure(1, facecolor='white')#创建一个图形
fig.clf()#
createPlot.ax1 = plt.subplot(111, frameon=False) #ticks for demo puropses
plotNode('a decision node', (0.5, 0.1), (0.1, 0.5), decisionNode)
plotNode('a leaf node', (0.8, 0.1), (0.3, 0.8), leafNode)
plt.show()
createPlot()
明天持续更新~
喜欢可以关注【小猪课堂】公众号了解更多内容