机器学习实战

KNN算法进行字母识别、决策树进行页面块分类

2018-10-27  本文已影响0人  YinliX

第一题:采用Knn算法分析letter Recognition Datasets数据集,要求测试集数目至少为50个

数据来源

https://archive.ics.uci.edu/ml/datasets/letter+recognition

运行环境

python3.7、numpy1.15.2、PyCharm 2018.2.4 (Community Edition)

思路

根据所给数据及其说明,共20000行数据,每行数据由‘,’分割开来,共17个数据,第一个数据为字母,后面16个为每个字母的特征数据,取前16000行数据作为训练数据,取后4000行数据作为测试数据。

代码结构

代码共包含三个函数,classify()、fileToMatrix(filename)和letterRecogonitionClassTest(),classify()用来将传入的特征向量进行分类并返回分类结果,fileToMatrix(filename)用来从文件中提取用于训练的特征矩阵和标签矩阵以及用于测试的特征矩阵和标签矩阵,letterRecogonitionClassTest()是主函数,分别分类4000行测试数据得到分类结果,与真实结果比较同时记录错误数目,最后给出错误数目和错误率。

源代码

# -*- coding: UTF-8 -*-
#Author:Yinli

import operator
import numpy as np

testingNum = 4000
trainingNum = 16000

"""
函数说明:从文件中提取用于训练的特征矩阵和标签矩阵以及用于测试的特征矩阵和标签矩阵

Parameters:
   filename
Returns:
   trainingMat, trainingLabels, testingMat, testingLabels
Modify:
   2018-10-03
"""
def fileToMatrix(filename):
    #读取文件
    fr = open(filename, 'r', encoding= 'utf-8')
    #读取每一行的数据
    arrayOfLines = fr.readlines()
    #返回trainingMat即用于训练的特征矩阵
    #返回trainingLabels即用于训练的标签矩阵
    #返回testingMat即用于测试的特征矩阵
    #返回testingLabels即用于训练的标签矩阵
    trainingMat = np.zeros((trainingNum, 16))
    testingMat = np.zeros((testingNum, 16))
    trainingLabels = []
    testingLabels = []
    #从前16000行提取用于训练的数据
    for i in range(trainingNum):
        arrayOfLines[i] = arrayOfLines[i].strip()
        listFromLine = arrayOfLines[i].split(',')
        trainingMat[i, :] = listFromLine[1:17]
        trainingLabels.append(listFromLine[0])
    #从后4000行提取用于测试的数据
    for i in range(testingNum):
        arrayOfLines[i+trainingNum] = arrayOfLines[i+trainingNum].strip()
        listFromLine = arrayOfLines[i+trainingNum].split(',')
        testingMat[i, :] = listFromLine[1:17]
        testingLabels.append(listFromLine[0])
    return trainingMat, trainingLabels, testingMat, testingLabels

"""
函数说明:将所传入的特征向量进行分类,返回分类结果

Parameters:
   inX————要进行分类的特征向量
   dataset————用于训练的特征矩阵
   labels————用于训练的标签向量
   k————kNN算法参数,选取距离最小的k个点
Returns:
    sortedClassCount[0][0]————分类结果 
Modify:
   2018-10-03
"""
def classify(inX, dataSet, labels, k):
    #np.tile用于构造一个每行相同的trainingNum*featureNum的矩阵
    diffMat = np.tile(inX, (trainingNum, 1)) - dataSet
    #二维特征相减之后将其平方
    sqDiffMat = diffMat ** 2
    #将平方之后得到的矩阵每行相加得到每行一个平方和
    sqDistances = sqDiffMat.sum(axis=1)
    #将此平方和开方
    distances = sqDistances**0.5
    #用argsort函数对这trainingNum行数据进行排序,返回索引值
    sortedDistIndices = distances.argsort()
    #构造一个用于记录类别次数的字典,键为类别,值为次数
    classCount = {}
    #取出欧式距离前k小的类别
    for i in range(k):
        #根据欧式距离第i小的索引值对应到labels,取出其对应的标签
        voteIlabel = labels[sortedDistIndices[i]]
        #在字典中查询此标签,若有则键值加一,没有则新增一个键,值为0再加一
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
    #对字典按值进行排序,降序排序,从大到小
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    #返回值最大的所对应的标签,为分类标签
    return sortedClassCount[0][0]

"""
函数说明:构建分类器并测试

Parameters:
   无
Returns:
   无
Modify:
   2018-10-03
"""
def letterRecogonitionClassTest():
    #从文件中分别读取用于训练的特征矩阵和标签矩阵以及用于测试的特征矩阵和标签矩阵
    filename = "D:\python_things\code\第2次作业\letter Recognition Datasets\letter-recognition.data"
    trainingMat, trainingLabels, testingMat, testingLabels = fileToMatrix(filename)
    #用于记录分类错误的数目
    errorCount = 0
    for i in range(testingNum):
        #提取用于测试的1*16向量
        vectorUnderTest = np.zeros((1, 16))
        for j in range(16):
            vectorUnderTest[0][j] = testingMat[i][j]
        #用classify函数来对此组数据进行分类,得到分类结果classifyResult
        classifyResult = classify(vectorUnderTest, trainingMat, trainingLabels, 3)
        print("分类结果为%c, 真实结果为%c" % (classifyResult, testingLabels[i]))
        #如果测试结果和真实结果不符,错误数加一
        if(classifyResult != testingLabels[i]):
            errorCount += 1
    print("共%d个数据,其中%d个数据用于训练,%d个数据用于训练" % (testingNum+trainingNum, trainingNum, testingNum))
    print("总共错了%d个数据\n错误率为%.3f%%" % (errorCount, errorCount/testingNum * 100))

if __name__ == '__main__':
    letterRecogonitionClassTest()

运行结果

result.png

第二题:采用决策树算法分析Page Blocks Classification Data Set 数据集,要求测试集数目至少为100个

数据来源

https://archive.ics.uci.edu/ml/datasets/Page+Blocks+Classification

运行环境

python3.7、numpy1.15.2、PyCharm 2018.2.4 (Community Edition)

思路

从数据及其说明可以看出,每组数据共有11个元素,前10个是其特征,最后一个是其标签,取1,2,3,4,5,分别代表text、horizontal line、picture、vertical line和graphic,总共5473组数据,考虑将前4000组数据用来训练模型,后1473组数据用来测试训练好的决策树。

代码构造

代码共分为三个文件,creatTree.py、plotTree.py和classification.py,creatTree.py用来根据数据集来构造决策树并将决策树保存起来,plotTree.py用于可视化决策树,classification.py用于读取决策树并用决策树来对测试集进行测试并输出结果。

源代码

createTree.py
# -*- coding: UTF-8 -*-
#Author:Yinli

import operator
import pickle
from math import log
import numpy as np
from plotTree import *

#数据总共5473组,其中将5000组用于训练模型,剩下的473组用来测试模型,每组数据一共10个特征和一个标签
totalNum = 5473
dataNum = 4000
testingNum = 1473

#将传入的数据文件处理拆分成dataSet矩阵和testingSet矩阵并返回
def fileToMatrix(filename):
   fr = open(filename, 'r', encoding='utf-8')
   arrayOfLines = fr.readlines()
   #dataSet和testingSet分别是用于训练决策树的数据和用于测试的数据
   dataSet = np.zeros((dataNum, 11))
   testingSet = np.zeros((totalNum - dataNum, 11))
   # 逐行处理训练数据,形成dataSet矩阵
   for i in range(dataNum):
      arrayOfLines[i] = arrayOfLines[i].strip()
      listFromLine = arrayOfLines[i].split()
      dataSet[i, :] = listFromLine[:]
   # 逐行处理测试数据,形成testingSet矩阵
   for i in range(testingNum):
      arrayOfLines[i + dataNum] = arrayOfLines[i + dataNum].strip()
      listFromLine = arrayOfLines[i + dataNum].split()
      testingSet[i, :] = listFromLine[:]
   return dataSet, testingSet

#对传入的classList列表统计每个标签的数目并排序返回数目最多的标签
def majorityCount(classList):
   #用classCount字典保存每个标签及其对应的数目
   classCount = {}
   #遍历classList中每个标签并统计数目
   for vote in classList:
      if vote not in classCount.keys():
         classCount[vote] = 0
      classCount[vote] += 1
   #将字典数据取出并按照字典值进行从大到小的排序,返回排好序的二维数组
   sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
   #返回数目最多的标签
   return sortedClassCount[0][0]

#对传入的数据矩阵根据最后一列数据计算其香农熵并返回
def calcShannonEnt(dataSet):
   #得到数据集数目,用labelCounts字典记录每个标签的数目
   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

#对传入的数据集按照指定的特征计算对这个特征而言最好的分离值并返回
def chooseBestSplitValue(dataSet, i):
   #featList记录此数据集中指定特征的所有值
   featList = [example[i] for example in dataSet]
   #将featList去重得到uniqueVals
   uniqueVals = set(featList)
   #将uniqueVals从小到大排序
   sortedUniqueVals = sorted(uniqueVals)
   #得到数据集的数目
   numOfData = len(dataSet)
   #用splitValueList记录待选的分离值
   splitValueList = []
   #初始化基础香农熵、最佳信息增益和最佳分离值
   baseEntropy = calcShannonEnt(dataSet)
   bestInfoGain = 0.0
   bestSplitValue = dataSet[0][i]
   #取sortedUniqueVals中每个值与其相邻值的平均值,记录在待选的分离值列表splitValueList中
   for j in range(len(sortedUniqueVals)-1):
      splitValueList.append((sortedUniqueVals[j]+sortedUniqueVals[j+1])/2.0)
   #遍历每个待选的分离值,选取最佳的分离值
   for k in range(len(splitValueList)):
      #根据当前分离值分离数据集并计算子数据集的香农熵和信息增益
      tempLeftDataSet, tempRightDataSet = splitDataSet(dataSet, i, splitValueList[k])
      tempLeftProb = len(tempLeftDataSet)/float(numOfData)
      tempRightProb = len(tempRightDataSet)/float(numOfData)
      tempEntropy = tempLeftProb*calcShannonEnt(tempLeftDataSet) + tempRightProb*calcShannonEnt(tempRightDataSet)
      tempInfoGain = baseEntropy - tempEntropy
      #若当前信息增益大于最佳信息增益,更新最佳分离值和最佳信息增益
      if(tempInfoGain > bestInfoGain):
         bestSplitValue = splitValueList[k]
         bestInfoGain = tempInfoGain
   #返回最佳分离值
   return bestSplitValue

#根据指定的特征和分离值对传入的数据集进行分离,返回两个子数据集
def splitDataSet(dataSet, axis, splitValue):
   #初始化两个子数据集
   leftDataSet = []
   rightDataSet = []
   #遍历每组数据
   for featVec in dataSet:
      #若这组数据的指定特征值小于等于分离值则将这组数据除此特征之外的所有值添加到左子数据集中
      if featVec[axis] <= splitValue:
         #临时向量先复制指定特征前的数据再复制指定特征后的数据
         reducedFeatVec = featVec[:axis]
         reducedFeatVec = np.append(reducedFeatVec, featVec[axis+1:])
         #将临时向量添加到左子数据集中
         leftDataSet.append(reducedFeatVec)
      #若这组数据的指定特征值大于分离值则将这组数据除此特征之外的所有值添加到右子数据集中
      else:
         #临时向量先复制指定特征前的数据再复制指定特征后的数据
         reducedFeatVec = featVec[:axis]
         reducedFeatVec = np.append(reducedFeatVec, featVec[axis+1:])
         #将临时向量添加到左子数据集中
         rightDataSet.append(reducedFeatVec)
   #返回分离后的两个子数据集
   return leftDataSet, rightDataSet

#对传入的数据集选择最好的分离特征并返回特征的索引值和分离后的子数据集以及分离值
def chooseBestFeatureToSplit(dataSet):
   #统计特征数目和数据集数目
   numOfFeaures = len(dataSet[0]) -1
   numOfData = len(dataSet)
   #初始化基础熵、最佳信息增益、最佳特征及其分离值
   baseEntropy = calcShannonEnt(dataSet)
   bestInfoGain = 0.0
   bestFeat = -1
   splitValueOfBestFeature = dataSet[0][0]
   #遍历每个特征
   for i in range(numOfFeaures):
      #基于此特征选取对应的最佳分离值
      splitValueOfI = chooseBestSplitValue(dataSet, i)
      #基于此特征及其最佳分离值将数据集分离为两个子数据集
      leftDataSetOfI, rightDataSetOfI = splitDataSet(dataSet, i, splitValueOfI)
      #计算子数据集的香农熵和信息增益
      lProb = len(leftDataSetOfI)/float(numOfData)
      rProb = len(rightDataSetOfI)/float(numOfData)
      newEntropy = lProb*calcShannonEnt(leftDataSetOfI) + rProb*calcShannonEnt(rightDataSetOfI)
      infoGain = baseEntropy - newEntropy
      #如果此特征及分离值的子数据集的信息增益大于最佳信息增益则更新最佳信息增益和最佳特征及其分离值
      if(infoGain > bestInfoGain):
         bestInfoGain = infoGain
         bestFeat = i
         splitValueOfBestFeature = splitValueOfI
   #基于最佳特征及其分离值分离数据集得到子数据集
   leftDataSet, rightDataSet = splitDataSet(dataSet, bestFeat, splitValueOfBestFeature)
   #返回最佳特征的索引和子数据集及对应的分离值
   return bestFeat, leftDataSet, rightDataSet, splitValueOfBestFeature

#对于传入的数据集和特征含义递归构造字典形式的决策树
def creatTree(dataSet, labels):
   #统计当前数据集的标签值
   classList = [example[-1] for example in dataSet]
   #如果当前标签全部统一则返回此标签
   if (classList.count(classList[0]) == len(classList)):
      return classList[0]
   #如果没有可进行分类的特征了则返回当前标签列表中数目最多的标签
   if ((len(dataSet[0]) == 1) or (len(labels) == 0)):
      return majorityCount(classList)
   #对于此数据集选取最佳的分离特征和对应的分离值并分离数据集
   bestFeat, leftDataSet, rightDataSet, splitValue = chooseBestFeatureToSplit(dataSet)
   #读取此特征的含义并用双下划线和其对应的分离值连接起来作为字典的键值,即分支节点的值
   bestFeatLabel = labels[bestFeat] + '__' + str(round(splitValue,3))
   myTree = {bestFeatLabel:{}}
   #在特征含义列表中删除已用特征
   del(labels[bestFeat])
   #如果左子数据集不为空则递归调用creatTree函数生成左子树
   #否则此分支节点无左子树,就直接返回此数据集的多数标签值
   #也就是将此分支节点和左子树合并为一个叶节点,不再进行分岔
   if(len(leftDataSet) > 0):
      #将特征含义列表拷贝到新列表,避免对原列表的修改
      leftSubLabels = labels[:]
      myTree[bestFeatLabel]['left'] = creatTree(leftDataSet, leftSubLabels)
   else:
      return majorityCount(classList)
   #如果右子数据集不为空则递归调用creatTree函数生成右子树
   #否则此分支节点无左子树,就直接返回此数据集的多数标签值
   #也就是将此分支节点和左子树合并为一个叶节点,不再进行分岔
   if(len(rightDataSet) > 0):
      rightSubLabels = labels[:]
      myTree[bestFeatLabel]['right'] = creatTree(rightDataSet, rightSubLabels)
   else:
      return majorityCount(classList)
   #返回构造的决策树字典
   return myTree

#用pickle存储决策树在对应的文件中
def storeTree(inputTree, filename):
   with open(filename, 'wb') as fw:
      pickle.dump(inputTree, fw)

#主函数
if __name__ == '__main__':
   #定义数据集文件名和存储决策树的文件名
   filename = "D:\python_things\code\第2次作业\Page Blocks Classification Data Set\page-blocks.data"
   treeName = "D:\python_things\code\第2次作业\Page Blocks Classification Data Set\myTree.pkl"
   #读取数据集文件并将其分为训练集和测试集
   dataSet, testingSet = fileToMatrix(filename)
   #定义标签含义列表和特征含义列表
   classifyLabels = ["text", "horizontal line", "picture", "vertical line" ,"graphic"]
   labels = ["height", "length", "area", "eccen", "p_black", "p_and", "mean_tr", "blackpix", "blackand", "wb_trans"]
   #对数据集构造决策树
   myTree = creatTree(dataSet, labels)
   #存储所构造的决策树
   storeTree(myTree, treeName)
plotTree.py
# -*- coding: UTF-8 -*-
#Author:Yinli

from matplotlib.font_manager import FontProperties
import matplotlib.pyplot as plt
import pickle

"""
函数说明:获取决策树叶子结点的数目

Parameters:
   myTree - 决策树
Returns:
   numLeafs - 决策树的叶子结点的数目
Author:
   Jack Cui
Blog:
   http://blog.csdn.net/c406495762
Modify:
   2017-07-24
"""
def getNumLeafs(myTree):
    numLeafs = 0                                    #初始化叶子
    firstStr = next(iter(myTree))                       #python3中myTree.keys()返回的是dict_keys,不在是list,所以不能使用myTree.keys()[0]的方法获取结点属性,可以使用list(myTree.keys())[0]
    secondDict = myTree[firstStr]                       #获取下一组字典
    for key in secondDict.keys():
        if type(secondDict[key]).__name__=='dict':          #测试该结点是否为字典,如果不是字典,代表此结点为叶子结点
            numLeafs += getNumLeafs(secondDict[key])
        else:   numLeafs +=1
    return numLeafs

"""
函数说明:获取决策树的层数

Parameters:
   myTree - 决策树
Returns:
   maxDepth - 决策树的层数
Author:
   Jack Cui
Blog:
   http://blog.csdn.net/c406495762
Modify:
   2017-07-24
"""
def getTreeDepth(myTree):
    maxDepth = 0                                    #初始化决策树深度
    firstStr = next(iter(myTree))                       #python3中myTree.keys()返回的是dict_keys,不在是list,所以不能使用myTree.keys()[0]的方法获取结点属性,可以使用list(myTree.keys())[0]
    secondDict = myTree[firstStr]                       #获取下一个字典
    for key in secondDict.keys():
        if type(secondDict[key]).__name__=='dict':          #测试该结点是否为字典,如果不是字典,代表此结点为叶子结点
            thisDepth = 1 + getTreeDepth(secondDict[key])
        else:   thisDepth = 1
        if thisDepth > maxDepth: maxDepth = thisDepth        #更新层数
    return maxDepth

"""
函数说明:绘制结点

Parameters:
   nodeTxt - 结点名
   centerPt - 文本位置
   parentPt - 标注的箭头位置
   nodeType - 结点格式
Returns:
   无
Author:
   Jack Cui
Blog:
   http://blog.csdn.net/c406495762
Modify:
   2017-07-24
"""
def plotNode(nodeTxt, centerPt, parentPt, nodeType):
   arrow_args = dict(arrowstyle="<-")                               #定义箭头格式
   font = FontProperties(fname=r"c:\windows\fonts\simsun.ttc", size=14)      #设置中文字体
   createPlot.ax1.annotate(nodeTxt, xy=parentPt,  xycoords='axes fraction',   #绘制结点
      xytext=centerPt, textcoords='axes fraction',
      va="center", ha="center", bbox=nodeType, arrowprops=arrow_args, FontProperties=font)

"""
函数说明:标注有向边属性值

Parameters:
   cntrPt、parentPt - 用于计算标注位置
   txtString - 标注的内容
Returns:
   无
Author:
   Jack Cui
Blog:
   http://blog.csdn.net/c406495762
Modify:
   2017-07-24
"""
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, va="center", ha="center", rotation=30)

"""
函数说明:绘制决策树

Parameters:
   myTree - 决策树(字典)
   parentPt - 标注的内容
   nodeTxt - 结点名
Returns:
   无
Author:
   Jack Cui
Blog:
   http://blog.csdn.net/c406495762
Modify:
   2017-07-24
"""
def plotTree(myTree, parentPt, nodeTxt):
   decisionNode = dict(boxstyle="sawtooth", fc="0.8")                            #设置结点格式
   leafNode = dict(boxstyle="round4", fc="0.8")                                 #设置叶结点格式
   numLeafs = getNumLeafs(myTree)                                            #获取决策树叶结点数目,决定了树的宽度
   depth = getTreeDepth(myTree)                                             #获取决策树层数
   firstStr = next(iter(myTree))                                            #下个字典
   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偏移
   for key in secondDict.keys():
      if type(secondDict[key]).__name__=='dict':                               #测试该结点是否为字典,如果不是字典,代表此结点为叶子结点
         plotTree(secondDict[key],cntrPt,str(key))                               #不是叶结点,递归调用继续绘制
      else:                                                           #如果是叶结点,绘制叶结点,并标注有向边属性值
         plotTree.xOff = plotTree.xOff + 1.0/plotTree.totalW
         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

"""
函数说明:创建绘制面板

Parameters:
   inTree - 决策树(字典)
Returns:
   无
Author:
   Jack Cui
Blog:
   http://blog.csdn.net/c406495762
Modify:
   2017-07-24
"""
def createPlot(inTree):
    fig = plt.figure(1, facecolor='white')                                     #创建fig
    fig.clf()                                                           #清空fig
    axprops = dict(xticks=[], yticks=[])
    createPlot.ax1 = plt.subplot(111, frameon=False, **axprops)                          #去掉x、y轴
    plotTree.totalW = float(getNumLeafs(inTree))                                 #获取决策树叶结点数目
    plotTree.totalD = float(getTreeDepth(inTree))                                #获取决策树层数
    plotTree.xOff = -0.5/plotTree.totalW; plotTree.yOff = 1.0;                      #x偏移
    plotTree(inTree, (0.5,1.0), '')                                              #绘制决策树
    plt.show()

#从文件中读取决策树并返回
def grabTree(filename):
   fr = open(filename, 'rb')
   return pickle.load(fr)

#主函数
if __name__ == '__main__':
   #读取决策树
   treeName = "D:\python_things\code\第2次作业\Page Blocks Classification Data Set\myTree.pkl"
   myTree = grabTree(treeName)
   #可视化决策树
   createPlot(myTree)
classification.py
# -*- coding: UTF-8 -*-
#Author:Yinli

import pickle
from creatTree import fileToMatrix

totalNum = 5473
dataNum = 4000
testingNum = 1473

#读取决策树并返回
def grabTree(filename):
   fr = open(filename, 'rb')
   return pickle.load(fr)

#对于传入的决策树和特征含义列表以及要预测的特征向量进行预测并返回预测标签索引值
def predict(inputTree, labels, vectorUnderPredict):
   #读取决策树的第一个键的值
   nodeName = list(inputTree.keys())[0]
   #将第一个键的值分开为特征含义和对应的分离值
   splitFeature = nodeName.split('__')[0]
   splitValue = nodeName.split('__')[1]
   #读取此分离特征的左子树和右子树
   leftDict = inputTree[nodeName]['left']
   rightDict = inputTree[nodeName]['right']
   #得到此分离特征在特征含义中的索引值
   featIndex = labels.index(splitFeature)
   #如果预测向量中对应的特征值小于等于分离值,则继续遍历左子树
   if vectorUnderPredict[featIndex] <= float(splitValue):
      #如果左子树为字典,即为分支节点,则继续调用predict函数遍历
      if type(leftDict).__name__ == 'dict':
         classLabel = predict(leftDict, labels, vectorUnderPredict)
      #如果左子树不为字典,即为叶节点,返回叶节点的值
      else:
         classLabel = leftDict
   #如果预测向量中对应的特征值大于分离值,则继续遍历右子树
   else:
      #如果右子树为字典,即为分支节点,则继续调用predict函数遍历
      if type(rightDict).__name__ == 'dict':
         classLabel = predict(rightDict, labels, vectorUnderPredict)
      #如果右子树不为字典,即为叶节点,返回叶节点的值
      else:
         classLabel = rightDict
   #返回预测标签索引值
   return classLabel

#主函数
if __name__ == '__main__':
   #定义数据集文件名和决策树文件名
   filename = "D:\python_things\code\第2次作业\Page Blocks Classification Data Set\page-blocks.data"
   treename = "D:\python_things\code\第2次作业\Page Blocks Classification Data Set\myTree.pkl"
   #处理数据集文件,将其分为训练集和测试集
   dataSet, testingSet = fileToMatrix(filename)
   #定义标签含义列表和特征含义列表
   classifyLabels = ["text", "horizontal line", "picture", "vertical line", "graphic"]
   labels = ["height", "length", "area", "eccen", "p_black", "p_and", "mean_tr", "blackpix", "blackand", "wb_trans"]
   #初始化预测错误的数目
   errorNum = 0
   #读取决策树
   myTree = grabTree(treename)
   #遍历测试集
   for i in range (testingNum):
      #对每组数据进行预测并返回分类结果索引值
      classifyResult = predict(myTree, labels, testingSet[i])
      #得到分类结果和实际结果并输出
      classifyResultString = classifyLabels[int(classifyResult)-1]
      testingResultString = classifyLabels[int(testingSet[i][-1])-1]
      print("分类结果为%s,实际结果为%s" % (classifyResultString, testingResultString))
      #如果分类结果和实际结果不同则错误数目加一
      if(classifyResult != testingSet[i][-1]):
         errorNum += 1

   #统计错误率并输出
   print("一共%d组数据,其中%d组数据用于训练,%d组数据用于测试" % (totalNum, dataNum, testingNum))
   print("一共%d组测试数据,判断错误%d组数据,错误率为%.3f%%" % (testingNum,  errorNum, errorNum/testingNum*100))

运行结果

plotTree.py
tree.png
classification.py
classification.png
上一篇下一篇

猜你喜欢

热点阅读