除了绘制树部分代码有借鉴,其他代码都是自己亲手完成,历时2天时间,过程稍微痛苦,当看到运行结果出现在面前时,一切都是高兴的,感觉过程也变得美妙了~

由于更喜欢C++编程,所以使用python类来完成~~~~,个人感觉面向对象更容易和更适合实现生成决策树的软件系统

由于是代码的第一稿,还没有精简,比较长,所以先贴出来运行结果,再给出代码

此软件系统:可以指定决策树的深度

1.使用的样本集

    ''''' 提供训练样本集 每个example由多个特征值+1个分类标签值组成 比如第一个example=['youth', 'no', 'no', '1', 'refuse'],此样本的含义可以解读为: 如果一个人的条件是:youth age,no working, no house, 信誉值credit为1 那么此类人会被分类到refuse一类中,即在相亲中被拒绝 每个example的特征值类型为: ['age', 'working', 'house', 'credit'] 每个example的分类标签class_label取值范围为:'refuse'或者'agree' '''  data_list = [['youth', 'no',  'no',   '1', 'refuse'],  ['youth', 'no',  'no',   '2', 'refuse'],  ['youth', 'yes', 'no',   '2', 'agree'],  ['youth', 'yes', 'yes',  '1', 'agree'],  ['youth', 'no',  'no',   '1', 'refuse'],  ['mid',   'no',  'no',   '1', 'refuse'],  ['mid',   'no',  'no',   '2', 'refuse'],  ['mid',   'yes', 'yes',  '2', 'agree'],  ['mid',   'no',  'yes',  '3', 'agree'],  ['mid',   'no',  'yes',  '3', 'agree'],  ['elder', 'no',  'yes',  '3', 'agree'],  ['elder', 'no',  'yes',  '2', 'agree'],  ['elder', 'yes', 'no',   '2', 'agree'],  ['elder', 'yes', 'no',   '3', 'agree'],  ['elder', 'no',  'no',   '1', 'refuse']]  feat_list = ['age', 'working', 'house', 'credit']  

2. 运行得到的样本集的决策树

=============== samples decision-tree dict ===================

len( samples decision-tree dict ): 1

type( samples decision-tree dict ): <class 'dict'>
np.shape( samples decision-tree dict ): ()
len(dict_shape): 0
samples decision-tree dict = {
         house :{
                 no :{
                         working :{
                                 no :{
                                         age : {'elder': 'refuse', 'youth': 'refuse'}
                                         }
                                 yes :  agree
                                 }
                         }
                 yes :  agree
                 }
         }
======

绘制的决策树图形

总上面可以看到,当决策树在age特征节点时,age的子节点都是refuse,说明此人不管什么年龄,都会被决绝,很容易知道age节点在决策树中没有任何意义,完全可以将working特征值=no时的子节点设置为refuse,下面我们对决策树的生成终止条件重新设置后,再看得到的决策树。设置leastFeatNum=4,上面决策树是leastFeatNum=3时生成的。

    #决策树生成终止的条件,当样本的特征少于leastFeatNum个时,不再继续求样本集的决策树leastFeatNum = 3#创建ID3算法生成决策树类的对象samples = CID3DecisionTree(train_data_list, feat_list, leastFeatNum)#生成决策树字典samples.create_tree()

leastFeatNum=4时,得到的决策树字典和绘制图形如下所示:

=============== samples decision-tree dict ===================
len( samples decision-tree dict ): 1
type( samples decision-tree dict ): <class 'dict'>
np.shape( samples decision-tree dict ): ()
len(dict_shape): 0
samples decision-tree dict = {
         house :{
                 no :{
                         working : {'no': 'refuse', 'yes': 'agree'}
                         }
                 yes :  agree
                 }
         }

======

可以看到,此时决策树更简洁更效率~

3.代码

# -*- coding: utf-8 -*-
"""
@author: 蔚蓝的天空Tom
Talk is cheap,show me the code
Aim:ID3算法生成决策树(字典存储), 并绘制决策树图形
"""  import numpy as np
import math
import matplotlib.pyplot as plt
varnamestr = lambda v,nms: [ vn for vn in nms if id(v)==id(nms[vn])][0]
class CUtileTool(object):  '''提供有用的方法 比如dump_list方法,可以打印给定的list的相关信息'''  def dump_list(self, src_list, src_list_namestr):  '''逐行打印list :param self:类实例自身 :param src_list:被打印的源list :return 无 '''  print('\n============',src_list_namestr,'================')  list_len = len(src_list)  list_shape = np.shape(src_list)  print('type(',src_list_namestr,'):',type(src_list))  #<class 'list'>  print('np.shape(',src_list_namestr,'):',np.shape(src_list))  if 1 == len(list_shape):  print(src_list)  elif 2 == len(list_shape):  for i in range(list_len):  if 0 == i:  print('[',src_list[i])  elif (list_len - 1) == i:  print(src_list[i],']')  else:  print(src_list[i])  else:  print(src_list)  print('======\n')  return  def dump_array(self, src_a, src_dict_namestr):  ''''' 逐行打印array :param self:类实例自身 :param src_a:被打印的源array :return 无 '''  print('\n===============',src_dict_namestr,'===================')  a_len = len(src_a)  a_shape = np.shape(src_a)  print('type(',src_dict_namestr,'):',type(src_a))  #<class 'list'>  print('np.shape(',src_dict_namestr,'):',np.shape(src_a))  if 1 == len(a_shape):  print(src_a)  elif 2 == len(a_shape):  for i in range(a_len):  if 0 == i:  print('[',src_a[i])  elif (a_len - 1) == i:  print(src_a[i],']')  else:  print(src_a[i])  else:  print(src_a)  print('======\n')  return  def print_dict(self, src_dict, level, src_dict_namestr=''):  ''''' 逐行打印dict :param self:类实例自身 :param src_dict:被打印的dict :param level:递归level,初次调用为level=0 :param src_dict_namestr:对象变量名称字符串 '''  if isinstance(src_dict, dict):  tab_str = '\t'  for i in range(level):  tab_str += '\t'  if 0 == level:  print(src_dict_namestr,'= {')  for key, value in src_dict.items():  if isinstance(value, dict):  has_dict = False  for k,v in value.items():  if isinstance(v, dict):  has_dict = True  if has_dict:  print(tab_str,key,":{")  self.print_dict(value, level + 1)  else:  print(tab_str,key,':',value)  else:  print(tab_str,key,': ',value,)  print(tab_str,'}')  def dump_dict(self, src_dict, src_dict_namestr):  ''''' 逐行打印dict :param self:类实例自身 :param src_dict:被打印的dict对象 :return 无 '''  print('\n===============',src_dict_namestr,'===================')  dict_len = len(src_dict)  dict_shape = np.shape(src_dict)  dict_type = type(src_dict)  print('len(',src_dict_namestr,'):',dict_len)  print('type(',src_dict_namestr,'):', dict_type)  #<class 'dict'>  print('np.shape(',src_dict_namestr,'):', dict_shape)  print('len(dict_shape):', len(dict_shape))  self.print_dict(src_dict, 0, src_dict_namestr)  print('======\n')  return  def dump(self, src_thing, src_thing_namestr):name = type(src_thing).__name__if name == 'list':return self.dump_list(src_thing, src_thing_namestr)  elif name == 'dict':return self.dump_dict(src_thing, src_thing_namestr)elif name == 'ndarray':print('hello')return self.dump_array(src_thing, src_thing_namestr)  else:print(src_thing_namestr,':', src_thing)  returnclass CDictHelper(object):'''dict helpera_dict = {'name':'Tom', 'age':18}'name':'Tom' is a item of a_dict'name' is key of item'Tom' is value of item'''def add(self, src_dict, key, value):if type(src_dict).__name__  != 'dict':print('error:expect type is dict, actual %', type(src_dict).__name__)return Falsesrc_dict[key] = valuereturn Truedef add_func(self, src_dict, key, value):'''dict[key] = value的向量化'''func = np.frompyfunc(self.add, 3, 1)return func(src_dict, key, value)def max_val_pair(self, src_dict, keys):if type(src_dict).__name__ != 'dict':print('type error:expace dict, acturl',type(src_dict).__name__)return Noneret_key, max_val = None, -9527for key in keys:if key in src_dict.keys():if src_dict[key] > max_val:ret_key, max_val = key, src_dict[key]return ret_key#def min_val_pair(self, src_dict, keys):if type(src_dict).__name__ != 'dict':print('type error:expace dict, acturl',type(src_dict).__name__)return Noneret_key, min_val = None, 9527for key in keys:if key in src_dict.keys():if src_dict[key] < min_val:ret_key, min_val = key, src_dict[key]return ret_keydef max_val_item(self, src_dict, parent_keys, child_key):'''max函数和min函数的判断操作符,必须有一个包含等于判断,否则会得出不正确的决策树1)>=, <         2)>, <='''if type(src_dict).__name__ != 'dict':print('type error:expace dict, acturl',type(src_dict).__name__)return Noneret_key, max_val = None, -9527for key in parent_keys:if key in src_dict.keys() and child_key in src_dict[key]:#if src_dict[key][child_key] >= max_val:#配对的min必须使用<if src_dict[key][child_key] > max_val:  #配对的min必须使用<=ret_key, max_val = key, src_dict[key][child_key]return ret_keydef min_val_item(self, src_dict, parent_keys, child_key):if type(src_dict).__name__ != 'dict':print('type error:expace dict, acturl',type(src_dict).__name__)return Noneret_key, min_val = None, 9527for key in parent_keys:if key in src_dict.keys() and child_key in src_dict[key]:#if src_dict[key][child_key] < min_val:#配对的max必须使用>=if src_dict[key][child_key] <= min_val:#配对的max必须使用>ret_key, min_val = key, src_dict[key][child_key]return ret_key'''CTailorSamples
裁剪规格简介
#每个样本example的特征列表
feature_type_list = ['youth','work','hourse','credit']
即每个样本=[age_value, work_value, housr_value, crdit_value, class_label]
如下一个样本集:
samples_list = [ ['youth', 'work_no', 'no', '1', 'refuse']['youth', 'work_no', 'no', '2', 'refuse']['youth', 'work_yes', 'no', '2', 'agree']['youth', 'work_yes', 'yes', '1', 'agree']['youth', 'work_no', 'no', '1', 'refuse']['mid', 'work_no', 'no', '1', 'refuse']['mid', 'work_no', 'no', '2', 'refuse']['mid', 'work_yes', 'yes', '2', 'agree']['mid', 'work_no', 'yes', '3', 'agree']['mid', 'work_no', 'yes', '3', 'agree']['elder', 'work_no', 'yes', '3', 'agree']['elder', 'work_no', 'yes', '2', 'agree']['elder', 'work_yes', 'no', '2', 'agree']['elder', 'work_yes', 'no', '3', 'agree']['elder', 'work_no', 'no', '1', 'refuse'] ]
假设已经通过信息增益选出此样本集的决策树的最优根节点为特征housre
如果想求子决策树的最优根节点的话,就需要对原始样本集进行裁剪了,然后用新的样本集筛选新的最优根节点
#通过如下规则得到新的样本集
step1:删除hourse特征值为yes所在的所有行
step2:然后再删除hourse特征值列
'''
class CTailorSamples(object):'''裁剪样本集'''def __init__(self, data_list, feat_type_list, feat_type_index, feat_value):self.data_list = data_listself.feat_type_list = feat_type_listself.feat_type_index_tailed = feat_type_indexself.feat_value_tailed = feat_valueself.tailer_work()#裁剪def get_samples(self):'''返回裁剪后的样本集,特征类型列表'''return self.data_list, self.feat_type_listdef get_all_indexs(self, src_list, dst_value):'''内部工具函数,请不要外部调用返回给定值的所有元素的下标src_list, e= [10,20,30,30,30,50], 30indexs_list = tailor.get_all_indexs(src_list, e)print(indexs_list) #[2, 3, 4]'''dst_val_index = [i for i,x in enumerate(src_list) if x == dst_value]return dst_val_indexdef tailer_work(self):'''内部工具函数,请不要外部调用裁剪得到新的特征列表'''del self.feat_type_list[self.feat_type_index_tailed]#摘取被删除的特征列colum_to_del = self.feat_type_index_tailedself.feat_value_list = [example[colum_to_del] for example in self.data_list]#找出含有self.feat_value_tailed特征值的所有样本所在行的下标rows_to_del = self.get_all_indexs(self.feat_value_list, self.feat_value_tailed)#删除row_index_list中行下标对应的self.src_data_list的行#技巧:从大的行下标开始依次删除#for row in list(reversed(rows_to_del)):#for row in rows_to_del[::-1]:rows_to_del.reverse()for row in rows_to_del:del self.data_list[row]#删除给定的特征列for row in range(len(self.data_list)):del self.data_list[row][colum_to_del]return self.data_list, self.feat_type_listclass CID3DecisionTree(object): '提供样本集的摘取目标数据的方法'  def __init__(self, data_list, feat_list, leastFeatNum):  ''''' 初始化函数 :param data_list:数据集 :param feat_list:数据的特征类型列表 :return 无 ''''''self.data_list 样例: [['youth', 'no',  'no',   '1', 'refuse'], ['youth', 'no',  'no',   '2', 'refuse']] '''self.data_list = data_list  '''self.feat_list样例['age', 'working', 'house', 'credit']'''  self.feat_list = feat_list  '''至少有leastFeatNum个特征,才求样本集的决策树'''self.leastFeatNum = leastFeatNum'''样本集的香农熵,H(Y={refuse, agree})'''  self.samples_shanon_entropy = 0.0'''''self.n_feats 就是每个样本的特征值类型总数'''  self.n_feats = len(feat_list)''''' self.feat_value_list 每种类型特征的取值列表, 样例: [['no','no','no','yes','no','no','no','yes','yes','yes','yes','yes','no','no','no'] ['1', '2', '2', '1', '1', '1', '2', '2', '3', '3', '3', '2', '2', '3', '1']] '''  self.feat_value_list = []''''' self.class_list 样本集的分类标签取值列表,长度为样本总数,样例: ['refuse', 'refuse', 'agree', …… ,'agree', 'agree', 'refuse'] '''  self.class_list = []  ''''' self.stat_dict 样本集的统计字典,样例: {age :{mid :{cnt :  5.0ent :  0.9709505944546686p_self :  0.3333333333333333refuse : {'cnt': 2, 'p_self': 0.4}agree : {'cnt': 3, 'p_self': 0.6}}info_gain :  0.08300749985576883elder :{cnt :  5.0ent :  0.7219280948873623p_self :  0.3333333333333333refuse : {'cnt': 1, 'p_self': 0.2}agree : {'cnt': 4, 'p_self': 0.8}}cond_ent :  0.8879430945988998youth :{cnt :  5.0ent :  0.9709505944546686p_self :  0.3333333333333333refuse : {'cnt': 3, 'p_self': 0.6}agree : {'cnt': 2, 'p_self': 0.4}}}working :{no :{cnt :  10.0ent :  0.9709505944546686p_self :  0.6666666666666666refuse : {'cnt': 6, 'p_self': 0.6}agree : {'cnt': 4, 'p_self': 0.4}}info_gain :  0.32365019815155627cond_ent :  0.6473003963031123yes :{cnt :  5.0ent :  0.0p_self :  0.3333333333333333refuse : {'cnt': 0, 'p_self': 0.0}agree : {'cnt': 5, 'p_self': 1.0}}}samples_ent :  0.9709505944546686credit :{1 :{cnt :  5.0ent :  0.7219280948873623p_self :  0.3333333333333333refuse : {'cnt': 4, 'p_self': 0.8}agree : {'cnt': 1, 'p_self': 0.2}}3 :{cnt :  4.0ent :  0.0p_self :  0.26666666666666666refuse : {'cnt': 0, 'p_self': 0.0}agree : {'cnt': 4, 'p_self': 1.0}}info_gain :  0.362989562537085362 :{cnt :  6.0ent :  0.9182958340544896p_self :  0.4refuse : {'cnt': 2, 'p_self': 0.3333333333333333}agree : {'cnt': 4, 'p_self': 0.6666666666666666}}cond_ent :  0.6079610319175832}house :{no :{cnt :  9.0ent :  0.9182958340544896p_self :  0.6refuse : {'cnt': 6, 'p_self': 0.6666666666666666}agree : {'cnt': 3, 'p_self': 0.3333333333333333}}info_gain :  0.4199730940219749cond_ent :  0.5509775004326937yes :{cnt :  6.0ent :  0.0p_self :  0.4refuse : {'cnt': 0, 'p_self': 0.0}agree : {'cnt': 6, 'p_self': 1.0}}}}''''''样本集的统计字典'''self.stat_dict = {}'''决策树字典, 供绘制者使用tree_dict = {house :{no :{working :{no :{age : {'elder': 'refuse', 'youth': 'refuse'}}yes :  agree}}yes :  agree}}'''self.tree_dict = {}self.pickout_feat()self.pickout_class()self.pickout_samples_shannon_entropy()self.build_stat_dict()return  #end of __init__()def get_example_cnt(self, feat_values, val, class_values, label):'''feat_values = ['high','high','high','low']class_values = ['refuse','refuse','agree','refuse']相同index时,特征值为high且分类值为refuse的总数,即2'''if type(feat_values).__name__ != 'list':print('type error:param1 expect list, actual', type(feat_values).__name__)return Noneif type(class_values).__name__ != 'list':print('type error:param2 expect list, actual', type(feat_values).__name__)return Noneif len(feat_values) != len(class_values):print('len error:param1 and param2 are of different length')return Noneret_cnt = 0for i in range(len(feat_values)):pair_tuple = (feat_values[i], class_values[i])if pair_tuple == (val, label):ret_cnt += 1return ret_cntdef shan_ent_ele(self, p):if 0 == p:  return 0else:  return -1 * p * math.log2(p)def shan_ent(self, P):'''样本概率, 如P=[0.4,0.6]'''func = np.frompyfunc(self.shan_ent_ele, 1, 1)  ent_ele_list = func(P)  entropy = ent_ele_list.sum() return entropydef pickout_feat(self):  '''摘取每种类型特征的特征值'''self.feat_value_list = []for dim in range(self.n_feats):  curtype_feat = [example[dim] for example in self.data_list]   self.feat_value_list.append(curtype_feat)return self.feat_value_listdef pickout_class(self): '''摘取分类列表,大小一定是m×1 '''self.class_list = [example[-1] for example in self.data_list]  return self.class_listdef pickout_samples_shannon_entropy(self):  '''计算样本集的香农熵,H(Y={refuse, agree})'''  #统计样本集的分类标签分布  label_set = set(self.class_list) label_cnt_list = [] for label in label_set:  label_cnt_list.append(self.class_list.count(label))  #统计样本集分类标签概率密度  n_samples = len(self.class_list)  label_prob_list = [label_cnt/n_samples for label_cnt in label_cnt_list]  #计算样本集的香农熵  self.samples_shanon_entropy = self.shan_ent(label_prob_list)  return self.samples_shanon_entropydef get_stat_dict(self):return self.stat_dictdef build_stat_dict(self):'''核心函数'''dh = CDictHelper()self.stat_dict= dict({})self.stat_dict['samples_ent'] = self.samples_shanon_entropyclass_values, class_set = self.class_list, set(self.class_list)for i in range(self.n_feats):#for feat in [age, house, work, cedit]feat, cond_ent, info_gain = self.feat_list[i], 0, 0 #feat, feat-cond-entropy, feat-info-gainfeat_values, value_set = self.feat_value_list[i], set(self.feat_value_list[i])for val in value_set:#for val in {youth, mid, elder}cnt, p_self = feat_values.count(val), feat_values.count(val)*1.0/len(feat_values)labels, s_dist, p_name, p_dens= [], [], [], []#样本变量,样本分布, 概率密度for label in class_set:#for label in {agree, refuse}labels.append(label)n_label = self.get_example_cnt(feat_values, val, class_values, label)s_dist.append(n_label)p_dens.append(n_label * 1.0 / cnt)p_name.append('p_' + label)shan_ent = self.shan_ent(p_dens)cond_ent += p_self * shan_ent#update feat cond-entropyinfo_gain = self.samples_shanon_entropy - cond_ent#填充字典if 'samples_ent' not in self.stat_dict.keys():self.stat_dict['samples_ent'] = self.samples_shanon_entropyif feat not in self.stat_dict.keys():self.stat_dict[feat] = {}dh.add(self.stat_dict[feat], val, dict({}))dh.add_func(self.stat_dict[feat][val], ['cnt', 'p_self', 'ent'], [cnt, p_self, shan_ent])for i in range(len(class_set)):if labels[i] not in self.stat_dict[feat][val].keys():self.stat_dict[feat][val][labels[i]] = {}self.stat_dict[feat][val][labels[i]]['cnt']    = s_dist[i]self.stat_dict[feat][val][labels[i]]['p_self'] = p_dens[i]pass#end of for label in class_set:#for label in {agree, refuse}dh.add_func(self.stat_dict[feat], ['cond_ent', 'info_gain'], [cond_ent, info_gain])pass#end of for val in value_set:#for val in {youth, mid, elder}pass#end of for i in range(self.n_feats):#for feat in [age, house, work, cedit]return self.stat_dictdef get_tree_dict(self):return self.tree_dictdef create_tree(self):'''对外核心函数'''dh = CDictHelper()#rootroot = dh.max_val_item(self.stat_dict, self.feat_list, 'info_gain')feat, feat_ind = root, self.feat_list.index(root)#lcond, rcondval_set = set(self.feat_value_list[feat_ind])rcond = value = dh.min_val_item(self.stat_dict[feat], val_set, 'ent')lcond = dh.max_val_item(self.stat_dict[feat], val_set, 'ent')#lnode, rnodeclass_set = set(self.class_list)rnode = dh.max_val_item(self.stat_dict[feat][value], class_set, 'p_self')lnode = dh.min_val_item(self.stat_dict[feat][value], class_set, 'p_self')#子树作为lnode,裁剪数据得到子树的样本数据if self.n_feats >= self.leastFeatNum:tailor = CTailorSamples(self.data_list, self.feat_list, feat_ind, value)new_samples_list, new_feat_list = tailor.get_samples()child_examples = CID3DecisionTree(new_samples_list, new_feat_list, self.leastFeatNum)lnode = child_examples.create_tree()#填充决策树self.tree_dict = {}self.tree_dict[root] = {}       self.tree_dict[root][rcond] = rnodeself.tree_dict[root][lcond] = lnodereturn self.tree_dict#定义判断结点形状,其中boxstyle表示文本框类型,fc指的是注释框颜色的深度
decisionNode = dict(boxstyle="round4", color='r', fc='0.9')
#定义叶结点形状
leafNode = dict(boxstyle="circle", color='m')
#定义父节点指向子节点或叶子的箭头形状
arrow_args = dict(arrowstyle="<-", color='g')
def plot_node(node_txt, center_point, parent_point, node_style):''' 内部函数,外部不要调用绘制父子节点,节点间的箭头,并填充箭头中间上的文本:param node_txt:文本内容:param center_point:文本中心点:param parent_point:指向文本中心的点'''createPlot.ax1.annotate(node_txt, xy=parent_point,xycoords='axes fraction',xytext=center_point,textcoords='axes fraction',va="center",ha="center",bbox=node_style,arrowprops=arrow_args)def get_leafs_num(tree_dict):'''内部函数,外部不要调用获取叶节点的个数:param tree_dict:树的数据字典:return tree_dict的叶节点总个数'''#tree_dict的叶节点总数leafs_num = 0if len(tree_dict.keys()) == 0:print('input tree dict is void!!!!!')return 0#字典的第一个键,也就是树的第一个节点root = list(tree_dict.keys())[0]#这个键所对应的值,即该节点的所有子树。child_tree_dict =tree_dict[root]for key in child_tree_dict.keys():#检测子树是否字典型if type(child_tree_dict[key]).__name__=='dict':#子树是字典型,则当前树的叶节点数加上此子树的叶节点数leafs_num += get_leafs_num(child_tree_dict[key])else:#子树不是字典型,则当前树的叶节点数加1leafs_num += 1#返回tree_dict的叶节点总数return leafs_numdef get_tree_max_depth(tree_dict):''' 内部函数,外部不要调用求树的最深层数:param tree_dict:树的字典存储:return tree_dict的最深层数'''#tree_dict的最深层数max_depth = 0if len(tree_dict.keys()) == 0:print('input tree_dict is void!')return 0#树的根节点root = list(tree_dict.keys())[0]#当前树的所有子树的字典child_tree_dict = tree_dict[root]for key in child_tree_dict.keys():#树的当前分支的层数this_path_depth = 0#检测子树是否字典型if type(child_tree_dict[key]).__name__ == 'dict':#如果子树是字典型,则当前分支的层数需要加上子树的最深层数this_path_depth = 1 + get_tree_max_depth(child_tree_dict[key])else:#如果子树不是字典型,则是叶节点,则当前分支的层数为1this_path_depth = 1if this_path_depth > max_depth:max_depth = this_path_depth#返回tree_dict的最深层数return max_depthdef plot_mid_text(center_point, parent_point, txt_str):'''内部函数,外部不要调用: 计算父节点和子节点的中间位置,并在父子节点间填充文本信息:param center_point:文本中心点:param parent_point:指向文本中心点的点'''x_mid = (parent_point[0] - center_point[0])/2.0 + center_point[0]y_mid = (parent_point[1] - center_point[1])/2.0 + center_point[1]createPlot.ax1.text(x_mid, y_mid, txt_str)returndef plotTree(tree_dict, parent_point, node_txt):'''内部函数,外部不要调用:绘制树:param tree_dict:树:param parent_point:父节点位置:param node_txt:节点内容'''leafs_num = get_leafs_num(tree_dict)root = list(tree_dict.keys())[0]#plotTree.totalW表示树的深度center_point = (plotTree.xOff+(1.0+float(leafs_num))/2.0/plotTree.totalW,plotTree.yOff)#填充node_txt内容plot_mid_text(center_point, parent_point, node_txt)#绘制箭头上的内容plot_node(root, center_point, parent_point, decisionNode)#子树child_tree_dict = tree_dict[root]plotTree.yOff=plotTree.yOff-1.0/plotTree.totalD#因从上往下画,所以需要依次递减y的坐标值,plotTree.totalD表示存储树的深度for key in child_tree_dict.keys():if type(child_tree_dict[key]).__name__ == 'dict':plotTree(child_tree_dict[key],center_point,str(key))else:plotTree.xOff=plotTree.xOff+1.0/plotTree.totalWplot_node(child_tree_dict[key],(plotTree.xOff,plotTree.yOff),center_point,leafNode)plot_mid_text((plotTree.xOff,plotTree.yOff),center_point,str(key))#h绘制完所有子节点后,增加全局变量Y的偏移plotTree.yOff=plotTree.yOff+1.0/plotTree.totalDreturndef createPlot(tree_dict):'''唯一对外函数:绘制决策树图形:param tree_dict:return 无'''#设置绘图区域的背景色fig=plt.figure(1,facecolor='white')#清空绘图区域fig.clf()#定义横纵坐标轴,注意不要设置xticks和yticks的值!!!axprops = dict(xticks=[], yticks=[])createPlot.ax1=plt.subplot(111, frameon=False, **axprops)#由全局变量createPlot.ax1定义一个绘图区,111表示一行一列的第一个,frameon表示边框,**axprops不显示刻度plotTree.totalW=float(get_leafs_num(tree_dict))plotTree.totalD=float(get_tree_max_depth(tree_dict))if plotTree.totalW == 0:print('tree_dict is void~')returnplotTree.xOff=-0.5/plotTree.totalW;plotTree.yOff=1.0;plotTree(tree_dict, (0.5,1.0), '')plt.show()def create_samples():  ''''' 提供训练样本集 每个example由多个特征值+1个分类标签值组成 比如第一个example=['youth', 'no', 'no', '1', 'refuse'],此样本的含义可以解读为: 如果一个人的条件是:youth age,no working, no house, 信誉值credit为1则此类人会被分类到refuse一类中,即在相亲中被拒绝(也可以理解为银行拒绝为此人贷款)每个example的特征值类型为: ['age', 'working', 'house', 'credit'] 每个example的分类标签class_label取值范围为:'refuse'或者'agree' '''  data_list = [['youth', 'no',  'no',   '1', 'refuse'],  ['youth', 'no',  'no',   '2', 'refuse'],  ['youth', 'yes', 'no',   '2', 'agree'],  ['youth', 'yes', 'yes',  '1', 'agree'],  ['youth', 'no',  'no',   '1', 'refuse'],  ['mid',   'no',  'no',   '1', 'refuse'],  ['mid',   'no',  'no',   '2', 'refuse'],  ['mid',   'yes', 'yes',  '2', 'agree'],  ['mid',   'no',  'yes',  '3', 'agree'],  ['mid',   'no',  'yes',  '3', 'agree'],  ['elder', 'no',  'yes',  '3', 'agree'],  ['elder', 'no',  'yes',  '2', 'agree'],  ['elder', 'yes', 'no',   '2', 'agree'],  ['elder', 'yes', 'no',   '3', 'agree'],  ['elder', 'no',  'no',   '1', 'refuse']]  feat_list = ['age', 'working', 'house', 'credit']  return data_list, feat_listif __name__=='__main__':  #创建样本集  train_data_list, feat_list = create_samples()#决策树生成终止的条件,当样本的特征少于leastFeatNum个时,不再继续求样本集的决策树leastFeatNum = 3#创建ID3算法生成决策树类的对象samples = CID3DecisionTree(train_data_list, feat_list, leastFeatNum)#生成决策树字典samples.create_tree()#打印样本集的统计字典CUtileTool().dump(samples.get_stat_dict(), 'samples statistic dict')#打印决策树字典CUtileTool().dump(samples.get_tree_dict(), 'samples decision-tree dict')#绘制决策树createPlot(samples.get_tree_dict())

enjoy it~~~

(end)

【机器学习】【决策树】ID3算法,Python代码实现生成决策树的系统相关推荐

  1. 决策树原理实例(python代码实现)_决策树原理实例(python代码实现)

    决策数(Decision Tree)在机器学习中也是比较常见的一种算法,属于监督学习中的一种.看字面意思应该也比较容易理解,相比其他算法比如支持向量机(SVM)或神经网络,似乎决策树感觉"亲 ...

  2. 机器学习决策树DecisionTree以及python代码实现

    机器学习决策树DecisionTree以及python代码实现 1.基本算法原理 2.选择最优特征进行划分 2.1信息增益 2.2信息增益率 2.3基尼系数 4.连续值以及缺失值的处理 4.1连续值的 ...

  3. 机器学习——使用ID3算法从原理到实际举例理解决策树

    文章目录 一.什么是决策树 二.介绍建立决策树的算法 三.决策树的一般流程 四.实际举例构建决策树 使用ID3算法的原理实现构建决策树 参考链接 一.什么是决策树 基本概念 决策树是一种树形结构,其组 ...

  4. 机器学习算法—决策树(ID3)算法

    机器学习--决策树(ID3)算法 1.决策树(ID3)算法 1.1 算法引入 我们首先以一个分类问题开始,假设我们有这样一份样本数据: 我们的目标是想通过色泽.根蒂.敲声.纹理.脐部.触感来判断这是不 ...

  5. 决策树---ID3算法

    决策树---ID3算法   决策树: 以天气数据库的训练数据为例. Outlook Temperature Humidity Windy PlayGolf? sunny 85 85 FALSE no ...

  6. 机器学习深度学习算法及代码实现

    原文地址:https://blog.csdn.net/qq_31456593/article/details/69340697 最近在学机器学习,学习过程中收获颇多,在此留下学习记录,希望与同道中人相 ...

  7. 多元线性回归算法python实现_手写算法-Python代码推广多元线性回归

    1.梯度下降-矩阵形式 上篇文章介绍了一元线性回归,包括Python实现和sklearn实现的实例.对比,以及一些问题点,详情可以看这里: 链接: 手写算法-Python代码实现一元线性回归 里面封装 ...

  8. 手写算法-python代码实现Ridge(L2正则项)回归

    手写算法-python代码实现Ridge回归 Ridge简介 Ridge回归分析与python代码实现 方法一:梯度下降法求解Ridge回归参数 方法二:标准方程法实现Ridge回归 调用sklear ...

  9. 进化算法——蛙跳算法Python代码

    进蛙跳算法Python代码 蛙跳算法原理 Python代码 参考文献 蛙跳算法原理 假设种群个数为 c h r o m n u m chromnum chromnum , 分组个数为 g r o u ...

  10. 机器学习题5:请简述ID3算法的实现步骤,并利用ID3算法构建天气数据集的决策树模型,实现决策树的可视化。

    ID3算法的实现步骤: 输入:数据集(训练集)S及属性A 输出:属性A对训练数据集S的信息增益 ① 先将S作为根节点,其目标属性y有c个类别属性.假设S中出现的概率,计算数据集S的信息熵. ② 假设属 ...

最新文章

  1. 智慧停车产业链市场全透析
  2. 转 当当网资深DBA:DB运维四大现代化的实现
  3. Linux升级openssh一次成功版本
  4. SAP UI5 应用开发教程之二:SAP UI5 的引导过程(Bootstrap)
  5. C++ decltype类型说明符(尾置返回类型使用)
  6. 简述java规范要注意哪些问题_JAVA学习:JAVA基础面试题(经典)
  7. mysql把一个数据库中的数据复制到另一个数据库中的表 2个表结构相同
  8. java switch case多个条件_JAVA基础程序设计之判断与循环
  9. thinkphp-查询某一列的值column
  10. angularjs源码笔记(4)--scope
  11. 汇率兑换 双向兑换_常出国的你一定要看:如何兑换外币最省钱?
  12. 辉煌十载!BDTC 2017 中国大数据技术大会在京盛大召开
  13. 对称矩阵(MIT课程)
  14. 网站后台扫描工具wwwscan、御剑、dirbuster、cansina的用法
  15. 笔记:全网最详细jQuery教程
  16. 如何登录华为云服务器
  17. 重拾Java基础知识:网络编程
  18. BruteXSS:XSS暴力破解神器
  19. Java计算机毕业设计腾讯网游辅助小助手源码+系统+数据库+lw文档
  20. Word中使用交叉引用插入多个参考文献

热门文章

  1. 【一点分享】Mysql高级特性:Event定时任务,用作定时调度ETL也很香。
  2. ARM学习系列 ---- ARM公司简介
  3. Libcurl最初的实现tfp上传和下载功能
  4. python黑底白字的车牌_Python+OpenCV实现车牌字符分割和识别
  5. oracle数据库exec用法,Sql中exec的用法
  6. IE编程1(.net)——读取IE窗口信息
  7. 无法使用内置管理员账户打开Microsoft Edge
  8. 某知乎x-zse-96参数解析2022/08/09首发
  9. js 实现html网页简繁体切换
  10. PHP批量上传一次点击选中多个