《机器学习实战》中关于决策树构建算法完全解析

《机器学习实战》中关于决策树构建算法完全解析:

```
#使用文本注解绘制树的节点
import matplotlib.pyplot as plt
import matplotlib

#这两行引入中文字库,中文字体为仿宋(这是我自己加的)
matplotlib.rcParams['font.sans-serif']=[u'FangSong']
matplotlib.rcParams['axes.unicode_minus']=False

#定义文本框和箭头格式
decisionNode=dict(boxstyle="sawtooth",fc="0.8")       #构建字典,描述树节点格式的常量
leafNode=dict(boxstyle="round4",fc="0.8")             #boxstyle设置的是结点文本框的形状sawtooth为锯齿形,fc为边框线粗细
arrow_args=dict(arrowstyle="<-")                    

#绘制带箭头的注解
def plotNode(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)

'''
annotate参数意义:
nodeTxt:数据点的文本名称
xy:指向文本的点
xycoords:设置为从左下角算起的轴分数
xyText:文本中心点
textcoords:同xycoords
va(verticalalignment):(设置文本框的数据中心点在文本框物理图案水平方向的哪边)水平对齐方式 ,可选参数 : 'center' , 'top' , 'bottom' ,'baseline' 
ha(horizontalalignment):(设置文本框的数据中心点在文本框物理图案垂直方向的哪边)垂直对齐方式,可选参数:'left','right','center'
bbox:结点的属性,即文本形状,线框粗细
arrowprops:设置的是箭头的形状(help函数查找可以得到形状类型)
'''

###定义图像(以下有更新该函数)
##def createPlot(myTree):
##    fig=plt.figure("第一图",facecolor='white')              #创建一个具体的绘图板
##    fig.clf()                                               #清空图像内容
##    createPlot.ax1=plt.subplot(111,frameon=False)           #"111"表示1行1列第一个,frameon表示是否显示坐标轴,默认为True,polar为极坐标,默认为False
##    plotNode('决策节点',(0.5,0.1),(0.1,0.5),decisionNode)   #见函数声明注解
##    plotNode('叶子节点',(0.8,0.1),(0.3,0.8),leafNode)
##    plt.show()

'''
函数内部定义全局变量的方法: createPlot.ax1=plt.subplot(111,frameon=False)
你想定义的变量首次被初始化的函数内,要<函数名>.<变量名>,在哪里都要声明该变量的域,
即使在被声明(初始化)的函数内部也是

'''

#得到叶节点的数量(不同于二叉树,不存在只有根节点的情况)
def getNumLeafs(myTree):
    numLeafs=0
    firstStr=list(myTree.keys())[0]               #得到myTree中的第一个键
    secondDict=myTree[firstStr]                   #得到myTree中第一个键的值(得到根节点)
    for key in secondDict.keys():                 #
        if type(secondDict[key]).__name__=='dict':#得到键对应值的类型名,并与'dict'比较
            numLeafs+=getNumLeafs(secondDict[key])#递归搜索叶节点个数
        else: numLeafs+=1                         #键的值非字典(非分节点)则为叶节点
    return numLeafs                               #返回叶节点个数

#得到树的深度
def getTreeDepth(myTree):
    maxDepth=0
    firstStr=list(myTree.keys())[0]
    secondDict=myTree[firstStr]
    for key in secondDict.keys():                 #对每一个分支进行遍历计数
        if type(secondDict[key]).__name__=='dict':#得到键对应值的类型名,并与'dict'比较
            thisDepth=1+getTreeDepth(secondDict[key])
        else: thisDepth=1                         #深究到底,出现叶节点
        if thisDepth>maxDepth:                    #比较当前最深与得到深度
            maxDepth=thisDepth                    #取最深的分支作为树深
    return maxDepth                               #返回树的深度

'''
说明一点:
python 2里面的:firstStr=list(myTree.keys())[0]得到字典的所有键不需要将myTree.keys()列表化,
python 3做了修改,否则报错:TypeError: 'dict_keys' object does not support indexing
'''

#定义一个地点列表
def retrieveTree(i):
    listOfTrees=[{'no surfacing':{0:'no',1:{'flippers':{0:'no',1:'yes'}}}},
                 {'no surfacing':{0:'no',1:{'filppers':{0:{'head':{0:'no',1:'yes'}},1:'no'}}}}]
    return listOfTrees[i]

#在父子节点间填充文本信息(计算父子结点的中间位置)
def plotMidText(cntrPt,parentPt,txtString):             #参数含义:子节点和父节点、文本内容
    xMid=(parentPt[0]-cntrPt[0])/2.0+cntrPt[0]
    yMid=(parentPt[1]-cntrPt[1])/2.0+cntrPt[1]
    createPlot.ax1.text(xMid,yMid,txtString)

#完成主要绘图步骤(这里的画板x、y的范围都是0-1)
def plotTree(myTree,parentPt,nodeTxt):                  #参数意义:字典、父节点、数据点的文本名称
    numLeafs=getNumLeafs(myTree)                        #计算树的叶子节点数目
    depth=getTreeDepth(myTree)                          #计算树深
    firstStr=list(myTree.keys())[0]                     #目标字典的键列表提取并得到第一个键(即根节点)
    cntrPt=(plotTree.xOff+(1.0+float(numLeafs))/2.0/plotTree.totalW,plotTree.yOff)#得到根节点(递归得到的是局部根节点)
    plotMidText(cntrPt,parentPt,nodeTxt)                #计算父子节点的中间位置并添加文本
    plotNode(firstStr,cntrPt,parentPt,decisionNode)     #第一个函数,绘制带箭头的注解,参数意义:节点文本、数据中心点(子节点)、父节点、节点框的属性
    secondDict=myTree[firstStr]                         #得到字典的第一个键(根节点)
    plotTree.yOff=plotTree.yOff-1.0/plotTree.totalD     #画完一层以后换层,y的值-1.0/树高(深)
    for key in secondDict.keys():                       #plotTree递归调用局部树的绘制构造整体,遍历第一个字典所有键并查看其属性
        if type(secondDict[key]).__name__=='dict':
            plotTree(secondDict[key],cntrPt,str(key))
        else:
            plotTree.xOff=plotTree.xOff+1.0/plotTree.totalW                        #plotTree.xOff的初始值为-0.6,目的是为了是图像在画板上对称,参数可调
            plotNode(secondDict[key],(plotTree.xOff,plotTree.yOff),cntrPt,leafNode)#绘制点
            plotMidText((plotTree.xOff,plotTree.yOff),cntrPt,str(key))             #线条上的文本信息
    plotTree.yOff=plotTree.yOff+1.0/plotTree.totalD

#plotTree的许多参数值(全局变量)都是在createPlot()中进行初始化
def createPlot(inTree):
    fig=plt.figure("决策树图解",facecolor='white')         #创建一个画板
    fig.clf()                                              #清除画板上的所有东西               
    axprops=dict(xticks=[],yticks=[])                      #创建字典
    createPlot.ax1=plt.subplot(111,frameon=False,**axprops) #ax1的性质是一个分区画板
    plotTree.totalW=float(getNumLeafs(inTree))             #得到叶子节点数目
    plotTree.totalD=float(getTreeDepth(inTree))            #得到树深
    plotTree.xOff=-0.5/plotTree.totalW                     #树结点摆放的位置x坐标:-0.5/树宽(叶子节点数目),负号的作用是调整图像位置,0.5参数是调整整个树的图像宽度
    plotTree.yOff=1.0                                      #y坐标设置为1.0
    plotTree(inTree,(0.5,1.0),'')
    plt.show()


'''
以上三个函数的解释:
createPlot()给函数plotTree()进行初始化,初始化根节点,字典的第一个键的位置和最大(最外围)字典的树高、树宽(叶节点数目)
            其中1/树高和1/树宽用于确定每个节点的坐标位置。
ploTree()得到字典的基本情况,得到根节点,绘制根节点,降层,遍历键的值:判断键的值是否为字典,若是字典,继续遍历;
            若不是字典则是根节点,直接绘制根节点,绘制根节点时候,层数(y坐标)已确定,横坐标在之前结点x的基础上加(往右)1/树宽
最后plot.show()绘制图形

'''
```

猜你喜欢

转载自blog.csdn.net/qq_43078427/article/details/87890835
今日推荐