西瓜书决策树实现(基于ID3)补充——采用自定义数据结构实现
·
刚刚写过一篇决策树实现的案列,树结构采用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
{'纹理': {'清晰': {'根蒂': {'蜷缩': '好瓜',
'稍蜷': {'色泽': {'青绿': '好瓜',
'乌黑': {'触感': {'硬滑': '好瓜', '软粘': '坏瓜'}},
'浅白': '好瓜'}},
'硬挺': '坏瓜'}},
'稍糊': {'触感': {'硬滑': '坏瓜', '软粘': '好瓜'}},
'模糊': '坏瓜'}}
结果与直接采用字典结构是一样的
此时可以方便的用现成的画图程序会出树结构

总结:采用自定义数据结构,可以更加灵活的决定节点存储的内容,比如还可以在节点内添加父节点指针,这在剪枝操作是概念非常清晰,也更容易实现。
更多推荐
所有评论(0)