刚刚写过一篇决策树实现的案列,树结构采用python自带的字典作为存储决策树的数据结构,为了下一步便于实现预剪枝和后剪枝,老叔认为采用自定义数据结构可能会更便于操作,也更灵活,可以在节点随意添加自己认为重要的元素。节点定义如下:

class Node(object):
    #定义节点类,类数据为样本列表,以及剩余属性列表
    def __init__(self, node_dataset, node_A):
        
        #节点的样本集合,可有可无
        self.node_dataset = node_dataset
        #节点的剩余特征集合
        self.node_A = node_A
        #叶结点标记,如果是叶结点,self.label为叶结点的标签值
        self.label = None
        #根结点标记,如果是根结点,self.ai_best为结点样本的最优增益特征
        self.ai_best = None
        #将子节点的指针为字典,key为特征标签,value为子节点地址
        self.child = {}

这时,决策树生成函数其实和字典差不多,不同的是此次函数返回的是一个根节点的地址,而不是字典。代码如下:

def TreeGenerate(node_dataset, A):
    
    #data,A生成节点
    node = Node(node_dataset, A)
    #如果样本标签相同,标记该节点为叶节点,节点标签为样本标签,并且返回节点指针
    if is_the_same_jugement(node_dataset):
        #节点标签为样本标签
        node.label =node_dataset[0]["标签"]
        return node
    #如果样本判别的剩余属性数量为0,将样本标签定义为数量最多的样本对应的标签,并且返回节点指针
    if len(A) == 0  or is_the_same_labels(node_dataset, A):
        node.label = argmax_jugement(node_dataset)
        return node
    
    #从A中选择最优划分属性a_best
    ai_best = select_ai(node_dataset, A)
    
    #将最优特征记录到节点内
    node.ai_best = ai_best
    
    #以ai_best属性将节点样本分类
    sub_node_datasets= classfide_by_ai_best(node_dataset,ai_best)
  
    #移除A中本次使用的最优特征
    A.remove(ai_best)
        #遍历ai_best 特征的标签值
    for ai_best_value in  sub_node_datasets.keys():
        #注意此时必须使用切片复制的方法,若直接采用sub_A = A,则sub_A中删除最优特征时,A中元素也会删除对应最优特征,
        #,导致同一个根节点的不同分支,随着递归进行,可选的剩余特征越来越少
        sub_A = A[:]
        
        #获取最大增益特征的标签值对应的样本列表
        sub_node_dataset =  sub_node_datasets[ai_best_value]
        #如果最大增益特征的某一个标签值对应的样本列表为空,将分支结点定为叶结点,判别标签标记为父节点的样本最多的类标签
        if len(sub_node_dataset) == 0:
            sub_node = Node(sub_node_dataset, A)
            sub_node.label = argmax_jugement(node_dataset)
            #节点的子节点指针设置为空
            sub_node.child = None
            #把根节点的指针指向本节点
            node.child[ai_best_value] = sub_node
            #继续生成下一个特征标签子节点
            continue  
            
        #递归调用本函数,函数自变量为子样本列表和剩余特征列表,函数返回判别标签
        
        node.child[ai_best_value] = TreeGenerate(sub_node_dataset, sub_A)
        
    return node

为了利用现成的画图程序,可以把节点结构的决策树转换为轻量级的字典,转换代码如下:

def node_to_dic(node):
    
    """
    Function : 把自定义数据结构转换为字典结构
    
    Input: node: 按照自定义的节点生成的决策树

    Output: 字典结构的决策树
           
    """
    
    #定义字典存储决策树
    my_tree = {}
    
    #如果是叶结点,直接返回样本标签
    if node.label:
        return node.label

    #如果是根结点,以该节点的最优特征作为字典中的key,value为一个嵌套字典,嵌套字典的key为最优特征的不同标签,
    #value为递归调用本函数返回的字典
    if node.ai_best:
        my_tree = {node.ai_best:{}}
        
        for ai_best_value in node.child.keys():
            my_tree[node.ai_best][ai_best_value] =  Travel(node.child[ai_best_value])
            
    return my_tree
    

运行一下

my_tree =  node_to_dic(mynode)
my_tree
{'纹理': {'清晰': {'根蒂': {'蜷缩': '好瓜',
    '稍蜷': {'色泽': {'青绿': '好瓜',
      '乌黑': {'触感': {'硬滑': '好瓜', '软粘': '坏瓜'}},
      '浅白': '好瓜'}},
    '硬挺': '坏瓜'}},
  '稍糊': {'触感': {'硬滑': '坏瓜', '软粘': '好瓜'}},
  '模糊': '坏瓜'}}

结果与直接采用字典结构是一样的
此时可以方便的用现成的画图程序会出树结构
在这里插入图片描述
总结:采用自定义数据结构,可以更加灵活的决定节点存储的内容,比如还可以在节点内添加父节点指针,这在剪枝操作是概念非常清晰,也更容易实现。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐