上一篇文章中主要介绍了如下几方面:html
本文以一个新的数据集(隐形眼镜数据集)为基础实现构建决策树、决策树的保存与加载、利用决策树分类、决策树的可视化,前文的知识不在过多概述,着重介绍这四个方面。python
先大体了解一下数据集:
这份数据源至UCI数据库,其共有4个特征分别为age(年龄)、prescript(症状)、astigmatic(闪光)、tearRate(泪液产生率)以及一个分类标签class,该分类包含硬材质、软材质和不该配带三种。git
为了方便处理,对样本作如下处理:算法
在构造决策树以前,先回顾一下前几个子模块的工做原理:先获取原始数据集,而后基于最优特征划分数据集,当数据集特征大于两个时,第一次划分以后,数据将被向下传递至树的下一个节点,在这个节点上,在此划分数据,此过程是利用递归原理处理数据集。数据库
何时划分结束呢?当程序遍历完全部划分数据集的属性,或者每一个分支下全部实例分类一致时表明划分数据集结束。windows
而构造决策树的过程就是将每一次划分出的数据填入一个字典中,当数据集划分结束时,向字典中填充数据也结束,此过程也是一个递归过程,至此决策树的构造完成。
代码以下:app
def CreateTree(DataSet): #获取全部特征标签 index_list = list(DataSet.columns) #获取最后一列(分类标签)的类别 label_series = DataSet.iloc[:,-1].value_counts() #判断类别标签最多一个是否等于数据样本数、或者数据集是否只有一列 if label_series[0]==DataSet.shape[0] or DataSet.shape[1] == 1: return label_series.index[0] #返回类标签 # 获取最优特征列索引 col = ChooseBF(DataSet) # 获取最优特征 BestFeature = index_list[col] #将最优特征依次填入字典中 TheTree = {BestFeature:{}} # 从标签列表中删去该特征标签 del index_list[col] #提取最佳切分列的全部属性值 value_list = set(DataSet.iloc[:,col]) #利用递归方法建树,每次对象为当前最优特征 for value in value_list: TheTree[BeatFeature][value] = CreateTree(splitSet(DataSet,col,value)) return TheTree
递归函数的第一个中止条件是全部的类标签都相同,递归函数第二个中止条件是使用完数据集中全部的特征,即数据集不能继续划分;字典变量TheTree储存了树的全部信息,BestFeature则是当前最优特征。ide
最后代码遍历当前最优特征的全部属性值,在每一个数据集划分上递归调用函数CreateTree(),而且传入的参数是每次划分以后的数据集,获得的返回值都会被插入字典TheTree中,递归结束后,字典中将会嵌套不少表明叶子节点信息的数据。函数
获得TheTree字典以下:工具
{'tearRate': {0: 'no lenses', 1: {'astigmatic': {0: {'age': {0: 'soft', 1: 'soft', 2: {'prescript': {0: 'no lenses', 1: 'soft'}}}}, 1: {'prescript': {0: 'hard', 1: {'age': {0: 'hard', 1: 'no lenses', 2: 'no lenses'}}}}}}}}
从左边开始,第一个关键字key为tearRate,这表明在全部特征中,tearRate特征的信息增益最大,在此特征下,数据降低(划分)最快,该关键字的值也是一个字典。第二个关键字是依据tearRate特征划分的数据集,这些关键字的值就是tearRate节点的子节点。
这些值多是类标签,也多是另外一个字典。若是值是类标签,则该子节点为叶子节点;若是值是另外一个字典,则该子节点是一个判断节点,经过这类格式不断重复就构成了一棵决策树。
决策树的保存有不少种方法,但原理都是同样的,即序列化与反序列化,这里介绍如下两种方法。
#第一种方法 np.save('TheTree.npy',TheTree) read_tree = np.load('TheTree.npy',allow_pickle=True).item()
第一种方法是利用numpy库中的save方法,能够将字典格式的决策树保存为npy文件;当读取树时,须要在方法后加上item(),由于咱们存储的数据是字典类型,如果矩阵类型则需删去。
#第二种方法 import pickle def storeTree(inputTree, filename): fw = open(filename,'wb') pickle.dump(inputTree,fw) fw.close() def grabTree(filename): fr = open(filename,'rb') return pickle.load(fr)
第二种方法是利用pickle库的dump方法对数据序列化,在读取时,用load方法便可加载数据,这里须要注意,不论写入仍是读取时,都须要以二进制的格式,否则会发生报错。
构造决策树以后,能够将它用于实际数据的分类,在执行数据分类时,须要传入决策树、特征标签列表和用于分类的测试数据。而后程序会比较测试数据与决策树上的数值,递归执行该过程直到进入叶子节点,最后获得的分类结果就是叶子节点所属类型。
代码以下:
#传入的数据为决策树、数据集特征标签、测试数据 def classify(inputTree,labels,testVec): #获取决策树的第一个节点 FirstStr = list(inputTree.keys())[0] #取第一个节点外下一个字典 SecondDict = inputTree[FirstStr] ''' {'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}} {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}} {0: 'no', 1: 'yes'} ''' #取第一个节点在labels里的索引 feat_index = labels.index(FirstStr) #遍历字典中的key for key in SecondDict.keys(): #比较testVec里的值与树节点的值,若是达到叶子节点,返回类标签 if testVec[feat_index]==key: #若是下一个字典中的仍包含字典,则递归继续比较 if type(SecondDict[key])==dict : classlabel = classify(SecondDict[key],labels,testVec) else: #直到最后取到类标签 classlabel = SecondDict[key] return classlabel
其中传入特征标签列表labels的做用是帮助肯定每次最优特征在数据集中的索引,利用index方法查找当前列表中第一个匹配FirstStr变量的元素,而后代码递归遍历整棵树,比较测试数据testVec变量中的值与树节点的值,直到达到叶子节点,返回当前节点的分类标签。
这里利用了上篇文章的数据构造的树作一个SecondDict举例,它的做用就是获取当前字典中最优特征(第一个关键字)的值,以达到与测试数据递归比较的效果。
classlabel = classify(inputTree,labels,[0,1,0,0]) ''' no lenses '''
执行该函数,能够将传入的数据与原文中的数据进行比对,获得的分类结果是一致的。
决策树的主要优势就是直观易于理解,若是不能将其直观地显示出来,就没法发挥其优点。但经过matplotlib库绘制决策树是一个十分复杂的过程,这里偷懒介绍另外一种比较简易的方法。
Graphviz是一种图形绘制工具,能够绘制出不少图形结构,但传入的数据须要的是dot格式,因此这里利用sklearn生成的决策树进行可视化。
Graphviz下载地址中下载graphviz-2.38.msi文件,在安装结束后须要配置环境,将该文件夹的路径添加至系统变量的Path中,在cmd中输入dot -version出现版本信息则表明安装配置成功。
决策树可视化代码以下:
from sklearn import tree from sklearn.tree import DecisionTreeClassifier import pydotplus import graphviz def pic_tree(DataSet): #全部特征数据 feature_train = DataSet.iloc[:, :-1] #类标签数据 the_label = DataSet.iloc[:, -1] #unique将类标签去重 labels = the_label.unique().tolist() #以类标签的在列表中的索引代替该标签——转化成数字 the_label = the_label.apply(lambda x: labels.index(x)) #训练数据 clf = DecisionTreeClassifier() clf = clf.fit(feature_train, the_label) #绘图过程 dot_data = tree.export_graphviz(clf, out_file=None, feature_names=['age','prescript','astigmatic','tearRate'], class_names=['no lenses', 'soft','hard'], filled=True, rounded=True, special_characters=True) # 两种方法 # 1.利用graphviz库生成PDF图片 pic = graphviz.Source(dot_data) pic.render('lense') # 2.利用pydotplus库将Dot格式转成PDF #graph = pydotplus.graph_from_dot_data(dot_data) #return graph.write_pdf("lense.pdf")
这里生成决策树图片时也有两种方法,第一种是利用graphviz库的Source方法生成PDF图片,第二种须要利用pydotplus库将Dot格式转成PDF,最后获得的可视化图片以下:
综上有关决策树的相关知识介绍完毕,整体来讲,这个分类算法仍是易于理解的,但它是十分重要的,由于它为后面学习随机森林奠基了基础,每个算法都有各自的适合环境,而决策树也有本身的优缺点。
决策树的优势:
决策树的缺点:
公众号【奶糖猫】后台回复“隐形眼镜”便可获取源码和数据供参考,感谢阅读。