决策树实现

上一篇博客记录了决策树构建的基本思想和构建的主要流程,这篇博客将介绍Python2.7下决策树算法的具体实现。

1.递归构建思路

  决策树构建的思路非常清晰,由函数treeGrow(dataset)的递归来实现决策树左右子树的构建,构建的顺序为1->2->3->4->5->6->7,与使用其他语言实现决策树算法没有差别。利用Python中的字典数据类型,通过key,value的赋值,多层嵌套字典,实现决策树的存储。
  决策树的构建有3个基本的结束条件:
(1)用于构建决策树的特征使用完毕
(2)叶子结点中的标签均为同一类别
(3)决策树构建完成

def treeGrow(dataset,features):
    if  len(classList) == classList.count(classList[0]):  #no more feature
        return classifyMaxLabel(classList)
    if  len(dataSet[0]) == 1:  # pure dataset
        return classList[0]
    bestFeature = findBestSplit(dataset)
    del bestFeature
    left_data,right_data = SplitDataset(dataset, bestFeature)
    tree['bestFeature-'] = treeGrow(left_data,features)
    tree['bestFeature+'] = treeGrow(right_data,features)
    return tree  # finish tree

python递归展示树 python输出递归树_决策树算法

2.具体实现

  决策树构建主要以TreeGrow函数为主体,构建以下几个函数来实现决策树构建。比较重要的函数为findBestSplit(dataSet)函数包含数据集信息熵、信息增益的计算,计算方法和原理在上一篇博客里有详细介绍,代码实现起来并不难。

函数名

功能

createDataSet(data)

创建数据集

splitDataSet(data,feature,value)

划分用来训练左右子树的数据集

findBestSplit(dataSet)

根据信息增益找到最合适的属性进行结点划分

calcShannonEnt(dataset)

按照属性分类计算数据的信息熵

classfy(classList)

找出类别列表中出现次数最多的类别

predict(tree,newObject)

实现决策树的预测功能

3.遇到的问题

  在运行时遇到了问题:决策树所有的右子树均为空
   
  原因分析:在PyCharm里调试程序,发现构建失败原因是构建右子树时,属性集feature_list为空。用于结点划分的属性存储在属性集feature_list中,在构建左右子树时均会使用属性集中的属性进行结点划分。决策树构建的递归算法与在C、C++其他语言实现是相同的,但C、C++中list为值类型,而Python中列表list为引用类型,在函数递归构建左子树的过程中使用过的属性会从feature_list中删除,feature_list的值会发生改变。因此,在构建右子树时,属性feature_list 为空,决策树所有的右子树没能完成构建。
  
  解决方法:通过copy模块的deepcopy函数对feature_list列表进行硬拷贝,拷贝在函数的当前层,在递归构建树的时候采用硬拷贝的属性集构建右子树。

right_features = copy.deepcopy(features)  # deepcopy the feature list
left_data,right_data = SplitDataset(dataset, bestFeature)
tree['bestFeature-'] = treeGrow(left_data,features)
tree['bestFeature+'] = treeGrow(right_data,right_features)

  小结: 关于列表是引用类型的问题,在之前的博客里也有提到过:Python多维数组初始化的两种方式和浅拷贝问题,但是具体运用时,还是经常会忽略这些细节。Python里的引用类型主要就是列表。元组不能改变其中的元素,只能整个元组一起删除或者和其他元组组合在一起。字典数据结构的value值要求是不能改变的数据类型,如整数、字符串。
  
  这个bug找了很久才找到原因,吃一堑长一智,以后对编程语言里的引用类型的操作要多加注意,避免出现类似的错误。