精华内容
下载资源
问答
  • 决策树算法

    千次阅读 多人点赞 2021-01-12 21:45:17
    决策树算法 一、决策树算法简介 二、决策树分类原理 1.熵 概念 案例 2.决策树的划分依据一----信息增益 概念 案例: 3.决策树的划分依据二----信息增益率 概念 案例 案例一 案例二 为什么...

    决策树算法

    一、决策树算法简介

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    二、决策树分类原理

    1.熵

    概念

    在这里插入图片描述
    在这里插入图片描述

    案例

    在这里插入图片描述
    在这里插入图片描述

    2.决策树的划分依据一----信息增益

    概念

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    案例:

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    3.决策树的划分依据二----信息增益率

    概念

    在这里插入图片描述

    案例

    案例一

    在这里插入图片描述
    在这里插入图片描述

    案例二

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    为什么使用C4.5要好

    在这里插入图片描述

    4.决策树的划分依据三 ----基尼值和基尼指数

    概念

    在这里插入图片描述

    案例

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    5.小结

    常见决策树的启发函数比较

    在这里插入图片描述
    在这里插入图片描述

    ID3 算法

    在这里插入图片描述

    C4.5算法

    在这里插入图片描述

    CART算法

    在这里插入图片描述

    多变量决策树(multi-variate decision tree)

    在这里插入图片描述

    决策树变量的两种类型:

    在这里插入图片描述

    如何评估分割点的好坏?

    在这里插入图片描述

    三、cart剪枝

    1.为什么要剪枝

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    2.常用的减枝方法

    在这里插入图片描述

    预剪枝

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    后剪枝:

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    3.小结

    在这里插入图片描述

    四、特征工程-特征提取

    在这里插入图片描述

    1.特征提取

    定义

    在这里插入图片描述

    特征提取API

    在这里插入图片描述

    2.字典特征提取

    在这里插入图片描述

    应用

    在这里插入图片描述

    流程分析

    在这里插入图片描述

    from sklearn.feature_extraction import DictVectorizer
    
    def dict_demo():
        """
        对字典类型的数据进行特征抽取
        :return: None
        """
        data = [{'city': '北京','temperature':100}, {'city': '上海','temperature':60}, {'city': '深圳','temperature':30}]
        # 1、实例化一个转换器类
        transfer = DictVectorizer(sparse=False)
        # 2、调用fit_transform
        data = transfer.fit_transform(data)
        print("返回的结果:\n", data)
        # 打印特征名字
        print("特征名字:\n", transfer.get_feature_names())
    
        return None
    

    在这里插入图片描述
    在这里插入图片描述

    总结

    对于特征当中存在类别信息的我们都会做one-hot编码处理

    3.文本特征提取

    在这里插入图片描述

    应用

    在这里插入图片描述

    流程分析

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    jieba分词处理

    在这里插入图片描述

    案例分析

    在这里插入图片描述

    from sklearn.feature_extraction.text import CountVectorizer
    import jieba
    
    def cut_word(text):
        """
        对中文进行分词
        "我爱北京天安门"————>"我 爱 北京 天安门"
        :param text:
        :return: text
        """
        # 用结巴对中文字符串进行分词
        text = " ".join(list(jieba.cut(text)))
    
        return text
    
    def text_chinese_count_demo2():
        """
        对中文进行特征抽取
        :return: None
        """
        data = ["一种还是一种今天很残酷,明天更残酷,后天很美好,但绝对大部分是死在明天晚上,所以每个人不要放弃今天。",
                "我们看到的从很远星系来的光是在几百万年之前发出的,这样当我们看到宇宙时,我们是在看它的过去。",
                "如果只用一种方式了解某样事物,你就不会真正了解它。了解事物真正含义的秘密取决于如何将其与我们所了解的事物相联系。"]
        # 将原始数据转换成分好词的形式
        text_list = []
        for sent in data:
            text_list.append(cut_word(sent))
        print(text_list)
    
        # 1、实例化一个转换器类
        # transfer = CountVectorizer(sparse=False)
        transfer = CountVectorizer()
        # 2、调用fit_transform
        data = transfer.fit_transform(text_list)
        print("文本特征抽取的结果:\n", data.toarray())
        print("返回特征名字:\n", transfer.get_feature_names())
    
        return None
    

    返回结果:

    Building prefix dict from the default dictionary ...
    Dumping model to file cache /var/folders/mz/tzf2l3sx4rgg6qpglfb035_r0000gn/T/jieba.cache
    Loading model cost 1.032 seconds.
    ['一种 还是 一种 今天 很 残酷 , 明天 更 残酷 , 后天 很 美好 , 但 绝对 大部分 是 死 在 明天 晚上 , 所以 每个 人 不要 放弃 今天 。', '我们 看到 的 从 很 远 星系 来 的 光是在 几百万年 之前 发出 的 , 这样 当 我们 看到 宇宙 时 , 我们 是 在 看 它 的 过去 。', '如果 只用 一种 方式 了解 某样 事物 , 你 就 不会 真正 了解 它 。 了解 事物 真正 含义 的 秘密 取决于 如何 将 其 与 我们 所 了解 的 事物 相 联系 。']
    Prefix dict has been built succesfully.
    文本特征抽取的结果:
     [[2 0 1 0 0 0 2 0 0 0 0 0 1 0 1 0 0 0 0 1 1 0 2 0 1 0 2 1 0 0 0 1 1 0 0 1 0]
     [0 0 0 1 0 0 0 1 1 1 0 0 0 0 0 0 0 1 3 0 0 0 0 1 0 0 0 0 2 0 0 0 0 0 1 0 1]
     [1 1 0 0 4 3 0 0 0 0 1 1 0 1 0 1 1 0 1 0 0 1 0 0 0 1 0 0 0 2 1 0 0 1 0 0 0]]
    返回特征名字:
     ['一种', '不会', '不要', '之前', '了解', '事物', '今天', '光是在', '几百万年', '发出', '取决于', '只用', '后天', '含义', '大部分', '如何', '如果', '宇宙', '我们', '所以', '放弃', '方式', '明天', '星系', '晚上', '某样', '残酷', '每个', '看到', '真正', '秘密', '绝对', '美好', '联系', '过去', '还是', '这样']
    

    在这里插入图片描述

    Tf-idf文本特征提取

    在这里插入图片描述

    公式

    在这里插入图片描述

    案例
    from sklearn.feature_extraction.text import TfidfVectorizer
    import jieba
    
    def cut_word(text):
        """
        对中文进行分词
        "我爱北京天安门"————>"我 爱 北京 天安门"
        :param text:
        :return: text
        """
        # 用结巴对中文字符串进行分词
        text = " ".join(list(jieba.cut(text)))
    
        return text
    
    def text_chinese_tfidf_demo():
        """
        对中文进行特征抽取
        :return: None
        """
        data = ["一种还是一种今天很残酷,明天更残酷,后天很美好,但绝对大部分是死在明天晚上,所以每个人不要放弃今天。",
                "我们看到的从很远星系来的光是在几百万年之前发出的,这样当我们看到宇宙时,我们是在看它的过去。",
                "如果只用一种方式了解某样事物,你就不会真正了解它。了解事物真正含义的秘密取决于如何将其与我们所了解的事物相联系。"]
        # 将原始数据转换成分好词的形式
        text_list = []
        for sent in data:
            text_list.append(cut_word(sent))
        print(text_list)
    
        # 1、实例化一个转换器类
        # transfer = CountVectorizer(sparse=False)
        transfer = TfidfVectorizer(stop_words=['一种', '不会', '不要'])
        # 2、调用fit_transform
        data = transfer.fit_transform(text_list)
        print("文本特征抽取的结果:\n", data.toarray())
        print("返回特征名字:\n", transfer.get_feature_names())
    
        return None
    

    返回结果:

    Building prefix dict from the default dictionary ...
    Loading model from cache /var/folders/mz/tzf2l3sx4rgg6qpglfb035_r0000gn/T/jieba.cache
    Loading model cost 0.856 seconds.
    Prefix dict has been built succesfully.
    ['一种 还是 一种 今天 很 残酷 , 明天 更 残酷 , 后天 很 美好 , 但 绝对 大部分 是 死 在 明天 晚上 , 所以 每个 人 不要 放弃 今天 。', '我们 看到 的 从 很 远 星系 来 的 光是在 几百万年 之前 发出 的 , 这样 当 我们 看到 宇宙 时 , 我们 是 在 看 它 的 过去 。', '如果 只用 一种 方式 了解 某样 事物 , 你 就 不会 真正 了解 它 。 了解 事物 真正 含义 的 秘密 取决于 如何 将 其 与 我们 所 了解 的 事物 相 联系 。']
    文本特征抽取的结果:
     [[ 0.          0.          0.          0.43643578  0.          0.          0.
       0.          0.          0.21821789  0.          0.21821789  0.          0.
       0.          0.          0.21821789  0.21821789  0.          0.43643578
       0.          0.21821789  0.          0.43643578  0.21821789  0.          0.
       0.          0.21821789  0.21821789  0.          0.          0.21821789
       0.        ]
     [ 0.2410822   0.          0.          0.          0.2410822   0.2410822
       0.2410822   0.          0.          0.          0.          0.          0.
       0.          0.2410822   0.55004769  0.          0.          0.          0.
       0.2410822   0.          0.          0.          0.          0.48216441
       0.          0.          0.          0.          0.          0.2410822
       0.          0.2410822 ]
     [ 0.          0.644003    0.48300225  0.          0.          0.          0.
       0.16100075  0.16100075  0.          0.16100075  0.          0.16100075
       0.16100075  0.          0.12244522  0.          0.          0.16100075
       0.          0.          0.          0.16100075  0.          0.          0.
       0.3220015   0.16100075  0.          0.          0.16100075  0.          0.
       0.        ]]
    返回特征名字:
     ['之前', '了解', '事物', '今天', '光是在', '几百万年', '发出', '取决于', '只用', '后天', '含义', '大部分', '如何', '如果', '宇宙', '我们', '所以', '放弃', '方式', '明天', '星系', '晚上', '某样', '残酷', '每个', '看到', '真正', '秘密', '绝对', '美好', '联系', '过去', '还是', '这样']
    

    Tf-idf的重要性

    分类机器学习算法进行文章分类中前期数据处理方式

    4.小结

    在这里插入图片描述

    五、决策树算法api

    在这里插入图片描述

    六、案例:泰坦尼克号乘客生存预测

    1.案例背景

    在这里插入图片描述

    2.步骤分析

    在这里插入图片描述

    3.代码实现

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    4.决策树可视化

    保存树的结构到dot文件

    在这里插入图片描述

    export_graphviz(estimator, out_file="./data/tree.dot", feature_names=['age', 'pclass=1st', 'pclass=2nd', 'pclass=3rd', '女性', '男性'])
    

    dot文件当中的内容如下

    digraph Tree {
    node [shape=box] ;
    0 [label="petal length (cm) <= 2.45\nentropy = 1.584\nsamples = 112\nvalue = [39, 37, 36]"] ;
    1 [label="entropy = 0.0\nsamples = 39\nvalue = [39, 0, 0]"] ;
    0 -> 1 [labeldistance=2.5, labelangle=45, headlabel="True"] ;
    2 [label="petal width (cm) <= 1.75\nentropy = 1.0\nsamples = 73\nvalue = [0, 37, 36]"] ;
    0 -> 2 [labeldistance=2.5, labelangle=-45, headlabel="False"] ;
    3 [label="petal length (cm) <= 5.05\nentropy = 0.391\nsamples = 39\nvalue = [0, 36, 3]"] ;
    2 -> 3 ;
    4 [label="sepal length (cm) <= 4.95\nentropy = 0.183\nsamples = 36\nvalue = [0, 35, 1]"] ;
    3 -> 4 ;
    5 [label="petal length (cm) <= 3.9\nentropy = 1.0\nsamples = 2\nvalue = [0, 1, 1]"] ;
    4 -> 5 ;
    6 [label="entropy = 0.0\nsamples = 1\nvalue = [0, 1, 0]"] ;
    5 -> 6 ;
    7 [label="entropy = 0.0\nsamples = 1\nvalue = [0, 0, 1]"] ;
    5 -> 7 ;
    8 [label="entropy = 0.0\nsamples = 34\nvalue = [0, 34, 0]"] ;
    4 -> 8 ;
    9 [label="petal width (cm) <= 1.55\nentropy = 0.918\nsamples = 3\nvalue = [0, 1, 2]"] ;
    3 -> 9 ;
    10 [label="entropy = 0.0\nsamples = 2\nvalue = [0, 0, 2]"] ;
    9 -> 10 ;
    11 [label="entropy = 0.0\nsamples = 1\nvalue = [0, 1, 0]"] ;
    9 -> 11 ;
    12 [label="petal length (cm) <= 4.85\nentropy = 0.191\nsamples = 34\nvalue = [0, 1, 33]"] ;
    2 -> 12 ;
    13 [label="entropy = 0.0\nsamples = 1\nvalue = [0, 1, 0]"] ;
    12 -> 13 ;
    14 [label="entropy = 0.0\nsamples = 33\nvalue = [0, 0, 33]"] ;
    12 -> 14 ;
    }
    

    那么这个结构不能看清结构,所以可以在一个网站上显示

    网站显示结构

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    5.决策树总结

    在这里插入图片描述

    6.小结

    在这里插入图片描述

    七、回归决策树

    在这里插入图片描述

    1.原理概述

    在这里插入图片描述
    在这里插入图片描述

    2.算法描述

    在这里插入图片描述

    3.简单实例

    在这里插入图片描述

    实例计算过程

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    回归决策树和线性回归对比

    import numpy as np
    import matplotlib.pyplot as plt
    from sklearn.tree import DecisionTreeRegressor
    from sklearn import linear_model
    
    # 生成数据
    x = np.array(list(range(1, 11))).reshape(-1, 1)
    y = np.array([5.56, 5.70, 5.91, 6.40, 6.80, 7.05, 8.90, 8.70, 9.00, 9.05])
    
    # 训练模型
    model1 = DecisionTreeRegressor(max_depth=1)
    model2 = DecisionTreeRegressor(max_depth=3)
    model3 = linear_model.LinearRegression()
    model1.fit(x, y)
    model2.fit(x, y)
    model3.fit(x, y)
    
    # 模型预测
    X_test = np.arange(0.0, 10.0, 0.01).reshape(-1, 1)  # 生成1000个数,用于预测模型
    X_test.shape
    y_1 = model1.predict(X_test)
    y_2 = model2.predict(X_test)
    y_3 = model3.predict(X_test)
    
    # 结果可视化
    plt.figure(figsize=(10, 6), dpi=100)
    plt.scatter(x, y, label="data")
    plt.plot(X_test, y_1,label="max_depth=1")
    plt.plot(X_test, y_2, label="max_depth=3")
    plt.plot(X_test, y_3, label='liner regression')
    
    plt.xlabel("data")
    plt.ylabel("target")
    plt.title("Decision Tree Regression")
    plt.legend()
    
    plt.show()
    

    结果展示
    在这里插入图片描述

    4.小结

    在这里插入图片描述

    展开全文
  • 最近布置了个课堂作业,用python实现决策树算法。整了几天勉勉强强画出了棵歪脖子树,记录一下。 大体思路: 1.创建决策树My_Decision_Tree类,类函数__init__()初始化参数、fit()进行决策树模型训练、predict()...

    最近布置了个课堂作业,用python实现决策树算法 。整了几天勉勉强强画出了棵歪脖子树,记录一下。

    大体思路:

    1.创建决策树My_Decision_Tree类,类函数__init__()初始化参数、fit()进行决策树模型训练、predict()进行预测、evaluate()进行模型评估、save_model()保存模型(csv格式)、load_model()加载模型、show_tree()使用Pillow库绘制决策树以及其他一些封装的功能函数;

    2.最佳划分点的度量通常有Gini值、Entropy、Classification error等,本程序采用Gini值,计算方法如下: 

    构建决策数模型所用到的功能函数(gini值计算相关)说明: 

    ①get_col_gini(self,threshold_point, value_series, label_series):计算一列数据在某一阈值划分点下的gini_split值;

    ②get_best_point(self,value_series, label_series):对连续型数据进行大小排序,分别计算切分点及其对应的gini_split值,找到使得gini_split值最小的最佳切分点;

    ③get_best_column(self,data,label_series):遍历数据表中的属性列,计算每列的最佳划分点和gini_split值,并对比找到最适合划分的一列及其对应的划分点;

    3.决策树构建(训练)的逻辑: 

    ①程序开始以root为父节点,先找到6列属性(剩余属性)中最适合划分的列和划分点,并将其划分为两个子节点,记录判断条件和此节点位置。删除子节点中的该列属性以避免最终决策树模型倾向于某个属性,利用剩余的属性继续构造决策树;

    ②判断各个子节点中的标签是否相同,如果为同一类则移到叶节点中,否则将此节点更新到下个流程中的父节点中;

    ③一直循环以上过程,当父节点为空时结束训练,如果所有子节点都为叶节点时则决策树完美将数据分类;当然也会出现所有属性都用完但子节点中标签依旧不唯一的情况,这时以该节点中个数较多的标签类作为分类结果,终止模型构建。

    # -*- coding: utf-8 -*-
    # @Version: Python 3.8.2
    # @Author: 707
    # @Use: 决策树算法编写
    
    import numpy as np
    import pandas as pd
    from sklearn.model_selection import train_test_split  #数据集划分
    #采用Pillow可视化决策树
    from PIL import Image
    from PIL import ImageDraw
    from PIL import ImageFont
    
    ###决策树框架###
    class My_Decision_Tree(object):
    	'''决策树框架'''
    
    	def __init__(self, arg=None):
    		##初始化类参数
    		self.arg = arg
    
    		#存放决策树中的各层判断条件
    		self.decision_df=pd.DataFrame(columns=['parent_depth','parent_pos','this_depth','this_pos','column','point','label'])
    		self.Parent_node_list=[]	#存放父节点和子节点的DataFrame
    		self.Child_node_list=[]	
    		self.leaf_list=[] #存放划分好的叶节点
    		self.branch_list=[]	#存放未能划分出来的节点
    
    	def fit(self,x_train,y_train):
    		'''传入训练集和标签构造决策树'''
    
    		'''		
    		程序逻辑:
    		(1)程序开始以root为父节点,先找到6列属性(剩余属性)中最适合划分的列和划分点,并将其划分为两个子节点,
    		记录判断条件和此节点位置。删除子节点中的该列属性以避免最终决策树模型倾向于某个属性,利用剩余的属性继续构造决策树
    		(2)判断各个子节点中的标签是否相同,如果为同一类则移到叶节点中,否则将此节点更新到下个流程中的父节点中
    		(3)一直循环以上过程,当父节点为空时结束训练,如果所有子节点都为叶节点时则决策树完美将数据分类;当然也会出现所有属性都用完但子节点
    		中标签依旧不唯一的情况,这时以该节点中个数较多的标签类作为分类结果,终止模型构建。
    
    		'''
    
    		x_data=x_train.copy()
    		if(y_train.name not in x_data.columns):
    			x_data[y_train.name]=y_train
    		
    		#把第一层(原数据)放入父节点列表Parent_node_list
    		self.Parent_node_list.append(x_data)
    		#写入第一层的决策(跟节点)
    		decision={'this_depth':1,'this_pos':0,'column':'root','label':'#'}
    		self.decision_df=self.decision_df.append(decision,ignore_index=True)
    		#开始循环计算分类节点
    		parent_count=0 #循环的父节点数
    		child_pos=0  #子节点的位置
    		depth=2 #第几层节点
    		while True:
    			parent_node=self.Parent_node_list[parent_count]
    			#找到第一个适合划分的列和划分点
    			col_1,point_1=self.get_best_column(parent_node,parent_node[y_train.name])
    			print('decision condition:',col_1,point_1)
    
    			#根据条件把父节点划分为两部分
    			Child_node1=parent_node.loc[parent_node[col_1]<=point_1]
    			Child_node2=parent_node.loc[parent_node[col_1]>point_1]
    			#每一部分的分类结果
    			result=[]
    			for child_node in [Child_node1,Child_node2]:
    				#删除已使用过的属性
    				del(child_node[col_1])
    
    				#判断子节点标签是否为一类,是则将其放入叶节点列表中
    				if(len(child_node[y_train.name].unique())==1):
    					self.leaf_list.append(child_node)
    					print('添加一个叶节点,标签类型:',child_node[y_train.name].unique()[0],'数据大小:',child_node.shape)
    					result.append(child_node[y_train.name].unique()[0])
    
    				# 判断子节点标签是否还有剩余属性可以作为分类依据,如果有则添加到子节点列表中用于后续分类
    				elif(child_node.shape[1]!=1):
    					self.Child_node_list.append(child_node)
    					print('添加一个子节点,数据大小:',child_node.shape)
    					result.append('#')
    				#都不满足说明该节点没有分完但是分不下去了,提示错误
    				else:
    					self.branch_list.append(child_node)
    					print('child_node节点已用完所有属性,仍然没分出来,剩余数据大小:')
    					print(child_node[y_train.name].value_counts())
    
    					values=child_node[y_train.name].value_counts()
    					if(len(values)==0):
    						replace=list(parent_node[y_train.name].value_counts().index)[0]
    					else:
    						replace=list(values.index)[0]
    					print('用%s作为该条件下的预测结果' %replace)
    					result.append(replace)
    
    			#找到该父节点在该层中所对应的位置
    			p_pos_list=self.decision_df.loc[(self.decision_df['this_depth']==depth-1)&(self.decision_df['label']=='#'),'this_pos']
    			p_pos_list=list(p_pos_list)
    			# print('p_pos_list:',p_pos_list)
    
    			#判断完一个父节点之后,把判断条件加入decision_df中  
    			decision1={'parent_depth':depth-1,'parent_pos':p_pos_list[parent_count],'this_depth':depth,'this_pos':child_pos,
    						'column':col_1,'point':point_1,'label':result[0]}
    			decision2={'parent_depth':depth-1,'parent_pos':p_pos_list[parent_count],'this_depth':depth,'this_pos':child_pos+1,
    						'column':col_1,'point':point_1,'label':result[1]}
    			self.decision_df=self.decision_df.append([decision1,decision2],ignore_index=True)
    
    			#当遍历完父节点列表所有值后,将子节点更新为父节点
    			child_pos+=2
    			parent_count+=1
    			if(parent_count==len(self.Parent_node_list)):
    				parent_count=0
    				child_pos=0
    				depth+=1
    				print('该层决策结束,进行下一层决策\n')
    				self.Parent_node_list=self.Child_node_list.copy()
    				self.Child_node_list.clear()
    				print('更新parent_node_list,大小:%d' %len(self.Parent_node_list))
    
    				#判断父节点列表中是否还有未分类的节点,如果没有则表示已经全部分好,结束训练
    				if(len(self.Parent_node_list)==0):
    					print('决策树构建完成')
    					#显示构建好的决策树:判断条件及结果(叶节点)
    					print(self.decision_df)
    
    					break
    
    	def predict(self,x_test):
    		'''输入测试数据进行决策判断'''
    		y_predict=list()
    		
    		if(type(x_test)==pd.core.series.Series):
    			pred=self.get_ylabel(x_test)
    			y_predict.append(pred)
    		else:
    			for index,row in x_test.iterrows():
    				pred=self.get_ylabel(row)
    				y_predict.append(pred)
    
    		y_predict=np.array(y_predict,dtype=str)
    		return y_predict
    
    
    
    	def evaluate(self,x_test,y_test):
    		'''输入测试集和标签评估决策树准确性,返回acc'''
    		y_true=np.array(y_test,dtype=str)
    		y_pred=self.predict(x_test)
    		# print(y_pred)
    		# print(y_true)
    		label_list=list(self.decision_df['label'].unique())
    		label_list.remove('#')
    		label_list=np.array(label_list,dtype=str) #类型转换
    		#创建混淆矩阵(index为true,columns为pred)
    		confusion_matrix=pd.DataFrame(data=0,columns=label_list,index=label_list)
    		for i in range(len(y_true)):
    			confusion_matrix.loc[y_true[i],y_pred[i]]+=1
    		print('混淆矩阵:')
    		print(confusion_matrix)
    		#计算准确率
    		acc=0
    		for i in range(len(label_list)):
    			acc+=confusion_matrix.iloc[i,i]
    		acc/=len(y_true)
    		print('acc:%.5f' %acc)
    		return acc
    
    	def save_model(self,path):
    		'''以csv格式保存模型'''
    		self.decision_df.to_csv(path,index=False)
    
    	def load_model(self,path):
    		'''以csv格式读取模型'''
    		self.decision_df=pd.read_csv(path)
    
    	def get_col_gini(self,threshold_point, value_series, label_series):
    	    '''Gini值计算函数'''
    
    	    # 将输入进行重组
    	    df_input = pd.DataFrame()
    	    df_input['value'] = value_series
    	    df_input['label'] = label_series
    	    # print(df_input)
    	    # 设计Gini值的计算表格
    	    label_cols = label_series.value_counts()
    	    df_gini = pd.DataFrame(columns=['node1', 'node2'], index=label_cols.index)
    	    
    	    for c in label_cols.index:
    	        df_c = df_input.loc[df_input['label'] == c]
    	        df_gini.loc[c, 'node1'] = df_c.loc[df_c['value']<= threshold_point].shape[0]
    	        df_gini.loc[c, 'node2'] = df_c.loc[df_c['value']> threshold_point].shape[0]
    
            #计算node1、node2节点gini值中和的部分
    	    sum_n1=df_gini['node1'].sum()
    	    sum_n2=df_gini['node2'].sum()
    	    # print(df_gini)
    
    	    # 计算node节点gini值
    	    gini_n1=gini_n2=0
    	    if(sum_n1==0):
    	    	for c in label_cols.index:
    	    		gini_n2+=(df_gini.loc[c,'node2']/sum_n2)**2
    	    elif(sum_n2==0):
    	    	for c in label_cols.index:
    	    		gini_n1+=(df_gini.loc[c,'node1']/sum_n1)**2
    	    else:
    	    	for c in label_cols.index:
    		    	gini_n1+=(df_gini.loc[c,'node1']/sum_n1)**2
    		    	gini_n2+=(df_gini.loc[c,'node2']/sum_n2)**2
    	    gini_n1 = 1-gini_n1
    	    gini_n2 = 1-gini_n2
    	    #计算gini_split
    	    gini_split=sum_n1/(sum_n1+sum_n2)*gini_n1 +sum_n2/(sum_n1+sum_n2)*gini_n2
    	    # print("point:%f,gini_split:%f" %(threshold_point,gini_split))
    	    return gini_split
    
    	def get_best_point(self,value_series, label_series):
    	    '''找到一列属性中最适合划分(gini值最小)的点'''
    
    	    value_array=np.array(value_series)
    	    value_array=np.sort(value_array)
    	    df_point = pd.DataFrame(columns=['point', 'gini_value'])
    
    	    # 循环属性值列,计算划分点及其gini值并添加至df_point数据表中
    	    for i in range(len(value_array) + 1):
    	        if(i == 0):
    	            point = value_array[i] - 1
    	        elif(i == len(value_array)):
    	            point = value_array[i - 1]
    	        else:
    	            point = 0.5 * (value_array[i] + value_array[i - 1])
    	        gini = self.get_col_gini(point, value_series, label_series)
    
    	        s = pd.Series(data={'point': point, 'gini_value': gini})
    	        df_point.loc[i] = s
    
    	    df_point.sort_values(by='gini_value', inplace=True)
    	    best_point = df_point.iloc[0, 0]
    	    best_gini = df_point.iloc[0,1]
    	    # print("best point for column '%s':%f" %(value_series.name,best_point))
    	    # print(df_point)
    	    return best_point,best_gini
    
    	def get_best_column(self,data,label_series):
    		'''遍历data中的属性列,计算其最佳划分点及gini值,找出最适合划分的一列和划分点'''
    		x_data=data.copy()
    		if(label_series.name in x_data.columns):
    			del(x_data[label_series.name])
    
    		gini_columns=pd.DataFrame(columns=['point','gini'],index=x_data.columns)
    		for col_name in x_data.columns:
    			point,gini=self.get_best_point(x_data[col_name],label_series)
    			s=pd.Series({'point':point,'gini':gini})
    			gini_columns.loc[col_name]=[point,gini]
    			# gini_columns=gini_columns.append(s,ignore_index=True)	#append会更改索引
    		gini_columns.sort_values(by='gini',inplace=True)
    		# print(gini_columns)
    		best_col=gini_columns.index[0]
    		best_point=gini_columns.iloc[0,0]
    		return best_col,best_point
    
    	def get_ylabel(self,x_series):
    		'''计算一行x数据(Series)对应的标签'''
    		model=self.decision_df
    
    		y_pred='#'
    		x_index=1
    		parent_index=[]
    		child_df=pd.DataFrame()
    
    		# for i in range(1):
    		while (y_pred=='#'):
    			#判断条件
    			condition=[model.loc[x_index,'column'],model.loc[x_index,'point']]
    			if(x_series[condition[0]]>condition[1]):
    				x_index+=1
    			# 	print('%s>%f' %(condition[0],condition[1]))
    			# else:
    			# 	print('%s<=%f' %(condition[0],condition[1]))
    				
    			y_pred=model.loc[x_index,'label']
    			#更新父节点索引并找到其子节点
    			parent_index=[model.loc[x_index,'this_depth'],model.loc[x_index,'this_pos']]
    			child_df=model.loc[(model['parent_depth']==parent_index[0])&(model['parent_pos']==parent_index[1])]
    			
    			#找到标签时结束
    			if(child_df.shape[0]!=0):
    				x_index=list(child_df.index)[0]
    			# 	print('跳到第%d行继续判断' %x_index)
    			# else:
    			# 	print('预测结束')
    		# print('pred:',y_pred)
    		return y_pred
    
    	def show_tree(self):
    		'''将决策树进行可视化'''
    
    		def add_text(im_draw,text_str,xy,multiline=1):
    			'''在绘图对象的某个位置添加文字'''
    			#设置大小
    			font_h,font_w=25,14
    			font_h*=multiline
    			text_len=round(len(text_str)/multiline)
    
    			font=ImageFont.truetype(font='simsun.ttc',size=20)
    			im_draw.text(xy=(xy[0]-font_w*3,xy[1]),text=text_str,font=font,fill='black',align='center')
    			#绘制矩形
    			# im_draw.rectangle(xy=(xy[0],xy[1],xy[0]+font_w*text_len,xy[1]+font_h),outline='black',width=2)
    
    		interval_x,interval_y=60,80
    		model=self.decision_df.copy()
    		model['x_pos']=model['this_pos']
    		model['y_pos']=(model['this_depth']-1)*interval_y
    		model['text']='text'
    		max_depth=model.iloc[-1,2]
    		
    		#创建图像
    		img_w,img_h=1500,600
    		tree_img=Image.new(mode='RGB',size=(img_w,img_h),color='white')
    		draw=ImageDraw.Draw(tree_img) #创建绘图对象
    
    		parent_pos=[]
    		parent_x_pos=0
    		x_pos=0
    		for x_index in model.index:
    			text=model.loc[x_index,'column']
    			if (str(model.loc[x_index,'point']) == 'nan'):
    				x_pos=img_w/4
    			else:
    				#跟新text内容和x位置
    				model.loc[x_index,'x_pos']=x_pos
    				parent_pos=[model.loc[x_index,'parent_depth'],model.loc[x_index,'parent_pos']]
    				parent_x_pos=model.loc[(model['this_depth']==parent_pos[0])&(model['this_pos']==parent_pos[1]),'x_pos']
    				depth=model.loc[x_index,'this_depth']-1
    				if(model.loc[x_index,'this_pos']%2==0):
    					text+='\n<='+('%.3f' %model.loc[x_index,'point'])
    					x_pos=parent_x_pos-interval_x*np.sqrt(max_depth-depth)
    
    				else:
    					text+='\n>'+('%.3f' %model.loc[x_index,'point'])
    					x_pos=parent_x_pos+interval_x*np.sqrt(max_depth-depth)
    				x_pos=x_pos.iloc[0]
    				if(model.loc[x_index,'label'] !='#'):
    					text+='\nClass:'+str(model.loc[x_index,'label'])
    			
    			#将文字和位置添加到
    			model.loc[x_index,'text']=text
    			model.loc[x_index,'x_pos']=x_pos
    		
    		# 调整节点横坐标位置
    		gap=140
    		for depth in model['this_depth'].unique():
    
    			if(depth!=1):
    				same_depth=model.loc[model['this_depth']==depth]
    				for x_index in same_depth.index[:-1]:
    					if(x_index==same_depth.index[0]):
    						if((model.loc[same_depth.index[0],'x_pos']-model.loc[same_depth.index[-1],'x_pos'])<gap*len(same_depth.index)):
    							#如果整体太挤,整层先往左移一段
    							for i in same_depth.index:
    								model.loc[i,'x_pos']-=gap*len(same_depth.index)/8
    					#如果相邻两个靠太近,右边的往右移一点
    					if((model.loc[x_index+1,'x_pos']-model.loc[x_index,'x_pos'])<gap):
    						model.loc[x_index+1,'x_pos']=model.loc[x_index,'x_pos']+gap
    						# model.loc[x_index,'x_pos']-=gap/2
    
    		#绘制文字和线
    		this_img_pos=[]
    		parent_img_pos=[]
    		for x_index in model.index:
    			#绘制直线
    			if(x_index !=0):
    				this_img_pos=[model.loc[x_index,'x_pos'],model.loc[x_index,'y_pos']]
    				parent_pos=[model.loc[x_index,'parent_depth'],model.loc[x_index,'parent_pos']]
    				parent_img_pos=model.loc[(model['this_depth']==parent_pos[0])&(model['this_pos']==parent_pos[1]),['x_pos','y_pos']]
    				parent_img_pos=[parent_img_pos.iloc[0,0],parent_img_pos.iloc[0,1]]
    				draw.line(xy=(parent_img_pos[0],parent_img_pos[1],this_img_pos[0],this_img_pos[1]),fill='gray',width=1)
    
    			#添加文字
    			this_pos=(model.loc[x_index,'x_pos'],model.loc[x_index,'y_pos'])
    			text=model.loc[x_index,'text']
    			add_text(im_draw=draw,text_str=text,xy=this_pos)
    		
    		#显示图片
    		tree_img.show()
    
    
    if __name__ == '__main__':
    
        # 读取文件
        data = pd.read_csv('./paras_labels.csv')
        #数据按8:2进行训练集、测试集切分
        x_train,x_test,y_train,y_test=train_test_split(data,data['label'],test_size=0.2,random_state=7)
    
    
        ds_tree=My_Decision_Tree()
    
        ds_tree.fit(x_train,y_train)
        # ds_tree.save_model('my_decision_tree%d.csv' %e)
    
        # ds_tree.load_model('my_decision_tree%d.csv' %e)
        # print(ds_tree.decision_df)
    
        print('训练集评估模型:')
        ds_tree.evaluate(x_train,y_train)
        print('测试集评估模型:')
        ds_tree.evaluate(x_test,y_test)
    
        ds_tree.show_tree()

    结果:

    用我的数据跑了一下,成功长出一棵歪脖子树,nice! 

     代码能力有限,有错误的地方欢迎大佬们交流,批评指正[狗头抱拳]

     

     

     

    展开全文
  • 决策树算法总结

    2020-12-26 01:29:38
    文章目录决策树算法决策树算法简介决策树分类原理熵案例信息增益——决策树的划分依据一信息增益率——决策树的划分依据二基尼值和基尼指数——决策树的划分依据三决策树原理小结总结:常见决策树类型比较1 ID3 算法2...

    决策树算法

    决策树算法简介

    决策树思想的来源非常朴素,程序设计中的条件分支结构就是if-else结构,最早的决策树就是利用这类结构分割数据的一种分类学习方法

    决策树:是一种树形结构,其中每个内部节点表示一个属性上的判断,每个分支代表一个判断结果的输出,最后每个叶节点代表一种分类结果,本质是一颗由多个判断节点组成的树

    怎么理解这句话?通过一个对话例子

    在这里插入图片描述

    想一想这个女生为什么把年龄放在最上面判断!!!!!!!!!

    上面案例是女生通过定性的主观意识,把年龄放到最上面,那么如果需要对这一过程进行量化,该如何处理呢?

    此时需要用到信息论中的知识:信息熵,信息增益


    决策树分类原理

    概念

    物理学上,熵 Entropy 是“混乱”程度的量度。

    在这里插入图片描述

    系统越有序,熵值越低;系统越混乱或者分散,熵值越高

    • 信息理论

    1、从信息的完整性上进行的描述:

    系统的有序状态一致时,**数据越集中的地方熵值越小,数据越分散的地方熵值越大。

    2、从信息的有序性上进行的描述:

    数据量一致时系统越有序,熵值越低;系统越混乱或者分散,熵值越高

    1948年香农提出了信息熵(Entropy)的概念。

    假如事件A的分类划分是(A1,A2,…,An),每部分发生的概率是(p1,p2,…,pn),那信息熵定义为公式如下:(log是以2为底,lg是以10为底)

    在这里插入图片描述

    案例

    课堂案例1:
    如果一颗骰子的六个面都是1 ,投掷它不会给你带来任何新信息,因为你知道它的结果肯定是1,它的信息熵为??
    
    答案:
     - log(1) = 0
    课堂案例2:
    假设我们没有看世界杯的比赛,但是想知道哪支球队会是冠军,
    我们只能猜测某支球队是或不是冠军,然后观众用对或不对来回答,
    我们想要猜测次数尽可能少,你会用什么方法?
    
    答案:
    二分法:
    假如有 16 支球队,分别编号,先问是否在 1-8 之间,如果是就继续问是否在 1-4 之间,
    以此类推,直到最后判断出冠军球队是哪只。
    如果球队数量是 16,我们需要问 4 次来得到最后的答案。那么世界冠军这条消息的信息熵就是 4。
    
    如果有32个球队,准确的信息量应该是: 
    H = -(p1 * logp1 + p2 * logp2 + ... + p32 * logp32),
    其中 p1, ..., p32 分别是这 32 支球队夺冠的概率。
    当每支球队夺冠概率相等都是 1/32 的时:H = -32 * 1/32 * log1/32= 5
    每个事件概率相同时,熵最大,这件事越不确定。
    
    随堂练习:
    篮球比赛里,有4个球队 {A,B,C,D} ,获胜概率分别为{1/2, 1/4, 1/8, 1/8}
    求H(X)
    
    答案:
    H(X) = 1\2log(2)+1\4log(4)+1\8log(8)+1\8log(8)=(1\2+1\2+3\8+3\8)log(2)=7\4bits
    tips:2为底,记做lb,单位bit
        以e为底,记做ln,单位nat
    

    信息增益——决策树的划分依据一

    概念

    信息增益:以某特征划分数据集前后的熵的差值。熵可以表示样本集合的不确定性,熵越大,样本的不确定性就越大。因此可以使用划分前后集合熵的差值来衡量使用当前特征对于样本集合D划分效果的好坏

    信息增益 = entroy(前) - entroy(后)

    • 定义与公式

    特征A对训练数据集D的信息增益g(D,A),定义为集合D的信息熵H(D)与特征A给定条件下D的信息条件熵H(D|A)之差,即公式为:

    在这里插入图片描述

    公式的详细解释:

    在这里插入图片描述

    注:信息增益表示得知特征X的信息而使得类Y的信息熵减少的程度

    案例:

    如下左图,第一列为论坛号码,第二列为性别,第三列为活跃度,最后一列用户是否流失。

    我们要解决一个问题:性别和活跃度两个特征,哪个对用户流失影响更大

    在这里插入图片描述

    通过计算信息增益可以解决这个问题,统计上右表信息

    其中Positive为正样本(已流失),Negative为负样本(未流失),下面的数值为不同划分下对应的人数。

    可得到三个熵:

    整体熵:

    在这里插入图片描述

    性别熵:

    在这里插入图片描述

    性别信息增益:

    在这里插入图片描述

    活跃度熵:

    在这里插入图片描述

    活跃度信息增益:

    在这里插入图片描述

    活跃度的信息增益比性别的信息增益大,也就是说,活跃度对用户流失的影响比性别大。

    在做特征选择或者数据分析的时候,我们应该重点考察活跃度这个指标。

    信息增益率——决策树的划分依据二

    **增益率:**增益比率度量是用前面的增益度量Gain(S,A)和所分离信息度量SplitInformation(如上例的性别,活跃度等)的比值来共同定义的。

    在这里插入图片描述

    基尼值和基尼指数——决策树的划分依据三

    概念

    **基尼值Gini(D):**从数据集D中随机抽取两个样本,其类别标记不一致的概率。故,Gini(D)值越小,数据集D的纯度越高。

    在这里插入图片描述

    **基尼指数Gini_index(D):**一般,选择使划分后基尼系数最小的属性作为最优化分属性。

    在这里插入图片描述

    案例

    请根据下图列表,按照基尼指数的划分依据,做出决策树。

    在这里插入图片描述

    1,对数据集非类标号属性{是否有房,婚姻状况,年收入}分别计算它们的Gini系数增益,取Gini系数增益值最大的属性作为决策树的根节点属性。

    2,根节点的Gini系数为:

    在这里插入图片描述

    3,当根据是否有房来进行划分时,Gini系数增益计算过程为:

    在这里插入图片描述

    在这里插入图片描述

    4,若按婚姻状况属性来划分,属性婚姻状况有三个可能的取值{married,single,divorced},分别计算划分后的Gini系数增益。

    {married} | {single,divorced}

    {single} | {married,divorced}

    {divorced} | {single,married}

    分组为{married} | {single,divorced}时:

    在这里插入图片描述

    当分组为{single} | {married,divorced}时:

    在这里插入图片描述

    当分组为{divorced} | {single,married}时:

    在这里插入图片描述

    对比计算结果,根据婚姻状况属性来划分根节点时取Gini系数增益最大的分组作为划分结果,即:{married} | {single,divorced}

    5,同理可得年收入Gini:

    对于年收入属性为数值型属性,首先需要对数据按升序排序,然后从小到大依次用相邻值的中间值作为分隔将样本划分为两组。例如当面对年收入为60和70这两个值时,我们算得其中间值为65。以中间值65作为分割点求出Gini系数增益。

    在这里插入图片描述

    最大化增益等价于最小化子女结点的不纯性度量(Gini系数)的加权平均值,现在我们希望最大化Gini系数的增益。根据计算知道,三个属性划分根节点的增益最大的有两个:年收入属性和婚姻状况,他们的增益都为0.12。此时,选取首先出现的属性作为第一次划分。

    6,接下来,采用同样的方法,分别计算剩下属性,其中根节点的Gini系数为(此时是否拖欠贷款的各有3个records)

    在这里插入图片描述

    7,对于是否有房属性,可得:

    在这里插入图片描述

    8,对于年收入属性则有:

    在这里插入图片描述

    在这里插入图片描述

    决策树原理小结

    一,决策树构建的基本步骤如下

    1. 开始将所有记录看作一个节点
    2. 遍历每个变量的每一种分割方式,找到最好的分割点
    3. 分割成两个节点N1和N2
    4. 对N1和N2分别继续执行2-3步,直到每个节点足够“纯”为止。

    二,决策树的变量可以有两种

    1. 数字型(Numeric):变量类型是整数或浮点数,如前面例子中的“年收入”。用“>=”,“>”,“<”或“<=”作为分割条件(排序后,利用已有的分割情况,可以优化分割算法的时间复杂度)。
    2. 名称型(Nominal):类似编程语言中的枚举类型,变量只能从有限的选项中选取,比如前面例子中的“婚姻情况”,只能是“单身”,“已婚”或“离婚”,使用“=”来分割。

    三,如何评估分割点的好坏?

    如果一个分割点可以将当前的所有节点分为两类,使得每一类都很“纯”,也就是同一类的记录较多,那么就是一个好分割点。

    比如上面的例子,“拥有房产”,可以将记录分成了两类,“是”的节点全部都可以偿还债务,非常“纯”;“否”的节点,可以偿还贷款和无法偿还贷款的人都有,不是很“纯”,但是两个节点加起来的纯度之和与原始节点的纯度之差最大,所以按照这种方法分割。构建决策树采用贪心算法,只考虑当前纯度差最大的情况作为分割点。

    总结:常见决策树类型比较

    在这里插入图片描述

    在这里插入图片描述

    1 ID3 算法

    存在的缺点

    (1) ID3算法在选择根节点和各内部节点中的分支属性时,采用信息增益作为评价标准。信息增益的缺点是倾向于选择取值较多的属性,在有些情况下这类属性可能不会提供太多有价值的信息.

    (2) ID3算法只能对描述属性为离散型属性的数据集构造决策树

    2 C4.5算法

    做出的改进(为什么使用C4.5要好)

    (1) 用信息增益率来选择属性

    (2) 可以处理连续数值型属性

    (3)采用了一种后剪枝方法

    (4)对于缺失值的处理

    C4.5算法的优缺点

    优点:

    产生的分类规则易于理解,准确率较高。

    缺点:

    在构造树的过程中,需要对数据集进行多次的顺序扫描和排序,因而导致算法的低效。

    此外,C4.5只适合于能够驻留于内存的数据集,当训练集大得无法在内存容纳时程序无法运行。

    3 CART算法

    CART算法相比C4.5算法的分类方法,采用了简化的二叉树模型,同时特征选择采用了近似的基尼系数来简化计算。

    C4.5不一定是二叉树,但CART一定是二叉树。

    同时,无论是ID3, C4.5还是CART,在做特征选择的时候都是选择最优的一个特征来做分类决策,但是大多数,**分类决策不应该是由某一个特征决定的,而是应该由一组特征决定的。**这样决策得到的决策树更加准确。这个决策树叫做多变量决策树(multi-variate decision tree)。在选择最优特征的时候,多变量决策树不是选择某一个最优特征,而是选择最优的一个特征线性组合来做决策。这个算法的代表是OC1,这里不多介绍。

    如果样本发生一点点的改动,就会导致树结构的剧烈改变。这个可以通过集成学习里面的随机森林之类的方法解决。


    cart剪枝

    为什么要剪枝

    在这里插入图片描述

    •横轴表示在决策树创建过程中树的结点总数,纵轴表示决策树的预测精度。

    •实线显示的是决策树在训练集上的精度,虚线显示的则是在一个独立的测试集上测量出来的精度。

    •随着树的增长,在训练样集上的精度是单调上升的, 然而在独立的测试样例上测出的精度先上升后下降。

    出现这种情况的原因:

    •原因1:噪声、样本冲突,即错误的样本数据。

    •原因2:特征即属性不能完全作为分类标准。

    •原因3:巧合的规律性,数据量不够大。

    常用的减枝方法

    预剪枝

    (1)每一个结点所包含的最小样本数目,例如10,则该结点总样本数小于10时,则不再分;

    (2)指定树的高度或者深度,例如树的最大深度为4;

    (3)指定结点的熵小于某个值,不再划分。随着树的增长, 在训练样集上的精度是单调上升的, 然而在独立的测试样例上测出的精度先上升后下降**。**

    后剪枝

    后剪枝,在已生成过拟合决策树上进行剪枝,可以得到简化版的剪枝决策树。


    决策树算法api

    class sklearn.tree.DecisionTreeClassifier(criterion=’gini’, max_depth=None,random_state=None)

    • criterion
      • 特征选择标准
      • “gini"或者"entropy”,前者代表基尼系数,后者代表信息增益。一默认"gini",即CART算法。
    • min_samples_split
      • 内部节点再划分所需最小样本数
      • 这个值限制了子树继续划分的条件,如果某节点的样本数少于min_samples_split,则不会继续再尝试选择最优特征来进行划分。 默认是2.如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。我之前的一个项目例子,有大概10万样本,建立决策树时,我选择了min_samples_split=10。可以作为参考。
    • min_samples_leaf
      • 叶子节点最少样本数
      • 这个值限制了叶子节点最少的样本数,如果某叶子节点数目小于样本数,则会和兄弟节点一起被剪枝。 默认是1,可以输入最少的样本数的整数,或者最少样本数占样本总数的百分比。如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。之前的10万样本项目使用min_samples_leaf的值为5,仅供参考。
    • max_depth
      • 决策树最大深度
      • 决策树的最大深度,默认可以不输入,如果不输入的话,决策树在建立子树的时候不会限制子树的深度。一般来说,数据少或者特征少的时候可以不管这个值。如果模型样本量多,特征也多的情况下,推荐限制这个最大深度,具体的取值取决于数据的分布。常用的可以取值10-100之间
    • random_state
      • 随机数种子

    案例:泰坦尼克号乘客生存预测

    泰坦尼克号数据

    在泰坦尼克号和titanic2数据帧描述泰坦尼克号上的个别乘客的生存状态。这里使用的数据集是由各种研究人员开始的。其中包括许多研究人员创建的旅客名单,由Michael A. Findlay编辑。我们提取的数据集中的特征是票的类别,存活,乘坐班,年龄,登陆,home.dest,房间,票,船和性别。

    数据:http://biostat.mc.vanderbilt.edu/wiki/pub/Main/DataSets/titanic.txt

    在这里插入图片描述

    经过观察数据得到:

    • 1 乘坐班是指乘客班(1,2,3),是社会经济阶层的代表。
    • 2 其中age数据存在缺失。

    步骤分析

    • 1.获取数据
    • 2.数据基本处理
      • 2.1 确定特征值,目标值
      • 2.2 缺失值处理
      • 2.3 数据集划分
    • 3.特征工程(字典特征抽取)
    • 4.机器学习(决策树)
    • 5.模型评估

    决策树可视化

    保存树的结构到dot文件
    • sklearn.tree.export_graphviz() 该函数能够导出DOT格式
      • tree.export_graphviz(estimator,out_file='tree.dot’,feature_names=[‘’,’’])
    export_graphviz(estimator, out_file="./data/tree.dot", feature_names=['age', 'pclass=1st', 'pclass=2nd', 'pclass=3rd', '女性', '男性'])
    

    dot文件当中的内容如下

    digraph Tree {
    node [shape=box] ;
    0 [label="petal length (cm) <= 2.45\nentropy = 1.584\nsamples = 112\nvalue = [39, 37, 36]"] ;
    1 [label="entropy = 0.0\nsamples = 39\nvalue = [39, 0, 0]"] ;
    0 -> 1 [labeldistance=2.5, labelangle=45, headlabel="True"] ;
    2 [label="petal width (cm) <= 1.75\nentropy = 1.0\nsamples = 73\nvalue = [0, 37, 36]"] ;
    0 -> 2 [labeldistance=2.5, labelangle=-45, headlabel="False"] ;
    3 [label="petal length (cm) <= 5.05\nentropy = 0.391\nsamples = 39\nvalue = [0, 36, 3]"] ;
    2 -> 3 ;
    4 [label="sepal length (cm) <= 4.95\nentropy = 0.183\nsamples = 36\nvalue = [0, 35, 1]"] ;
    3 -> 4 ;
    5 [label="petal length (cm) <= 3.9\nentropy = 1.0\nsamples = 2\nvalue = [0, 1, 1]"] ;
    4 -> 5 ;
    6 [label="entropy = 0.0\nsamples = 1\nvalue = [0, 1, 0]"] ;
    5 -> 6 ;
    7 [label="entropy = 0.0\nsamples = 1\nvalue = [0, 0, 1]"] ;
    5 -> 7 ;
    8 [label="entropy = 0.0\nsamples = 34\nvalue = [0, 34, 0]"] ;
    4 -> 8 ;
    9 [label="petal width (cm) <= 1.55\nentropy = 0.918\nsamples = 3\nvalue = [0, 1, 2]"] ;
    3 -> 9 ;
    10 [label="entropy = 0.0\nsamples = 2\nvalue = [0, 0, 2]"] ;
    9 -> 10 ;
    11 [label="entropy = 0.0\nsamples = 1\nvalue = [0, 1, 0]"] ;
    9 -> 11 ;
    12 [label="petal length (cm) <= 4.85\nentropy = 0.191\nsamples = 34\nvalue = [0, 1, 33]"] ;
    2 -> 12 ;
    13 [label="entropy = 0.0\nsamples = 1\nvalue = [0, 1, 0]"] ;
    12 -> 13 ;
    14 [label="entropy = 0.0\nsamples = 33\nvalue = [0, 0, 33]"] ;
    12 -> 14 ;
    }
    

    那么这个结构不能看清结构,所以可以在一个网站上显示

    网站显示结构
    • http://webgraphviz.com/

    在这里插入图片描述

    将dot文件内容复制到该网站当中显示

    在这里插入图片描述

    实现过程

    • 导入需要的模块
    import pandas as pd
    import numpy as np
    from sklearn.feature_extraction import DictVectorizer
    from sklearn.model_selection import train_test_split
    from sklearn.tree import DecisionTreeClassifier, export_graphviz
    
    • 1.获取数据
    # 1、获取数据
    titan = pd.read_csv("../data/titanic.txt")
    
    • 2.数据基本处理

      • 2.1 确定特征值,目标值
      x = titan[["pclass", "age", "sex"]]
      y = titan["survived"]
      
      • 2.2 缺失值处理
      # 缺失值需要处理,将特征当中有类别的这些特征进行字典特征抽取
      x['age'].fillna(x['age'].mean(), inplace=True)
      
      • 2.3 数据集划分
      x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=22)
      
    • 3.特征工程(字典特征抽取)

    特征中出现类别符号,需要进行one-hot编码处理(DictVectorizer)

    x.to_dict(orient=“records”) 需要将数组特征转换成字典数据

    # 对于x转换成字典数据x.to_dict(orient="records")
    # [{"pclass": "1st", "age": 29.00, "sex": "female"}, {}]
    
    transfer = DictVectorizer(sparse=False)
    
    x_train = transfer.fit_transform(x_train.to_dict(orient="records"))
    x_test = transfer.fit_transform(x_test.to_dict(orient="records"))
    
    • 4.决策树模型训练和模型评估

    决策树API当中,如果没有指定max_depth那么会根据信息熵的条件直到最终结束。这里我们可以指定树的深度来进行限制树的大小

    # 4.机器学习(决策树)
    estimator = DecisionTreeClassifier(criterion="entropy", max_depth=5)
    estimator.fit(x_train, y_train)
    
    # 5.模型评估
    estimator.score(x_test, y_test)
    print(score)
    
    #保存树的结构到dot文件
    export_graphviz(estimator, out_file="../data/titan_tree.dot", feature_names=['age', 'pclass=1st', 'pclass=2nd', 'pclass=3rd', '女性', '男性'])
    

    决策树总结

    • 优点:
      • 简单的理解和解释,树木可视化。
    • 缺点:
      • 决策树学习者可以创建不能很好地推广数据的过于复杂的树,容易发生过拟合。
    • 改进:
      • 减枝cart算法
      • 随机森林(集成学习的一种)

    注:企业重要决策,由于决策树很好的分析能力,在决策过程应用较多, 可以选择特征


    特征工程-特征提取

    什么是特征提取呢?

    在这里插入图片描述

    在这里插入图片描述

    特征提取

    定义

    将任意数据(如文本或图像)转换为可用于机器学习的数字特征

    注:特征值化是为了计算机更好的去理解数据

    特征提取分类:

    • 字典特征提取(特征离散化)
    • 文本特征提取
    • 图像特征提取(深度学习将介绍)

    特征提取API

    sklearn.feature_extraction
    

    字典特征提取

    作用:对字典数据进行特征值化

    • sklearn.feature_extraction.DictVectorizer(sparse=True,…)
      • DictVectorizer.fit_transform(X)
        • X:字典或者包含字典的迭代器返回值
        • 返回sparse矩阵
      • DictVectorizer.get_feature_names() 返回类别名称

    应用

    我们对以下数据进行特征提取

    [{'city': '北京','temperature':100},
    {'city': '上海','temperature':60},
    {'city': '深圳','temperature':30}]
    

    在这里插入图片描述

    流程分析

    • 实例化类DictVectorizer
    • 调用fit_transform方法输入数据并转换(注意返回格式)
    from sklearn.feature_extraction import DictVectorizer
    
    def dict_demo():
        data =  [{'city': '北京','temperature':100}, {'city': '上海','temperature':60}, {'city': '深圳','temperature':30}]
    
        #transfer = DictVectorizer()
        transfer = DictVectorizer(sparse=False)
        data = transfer.fit_transform(data)
        print(data)
        print('*'*20)
        print(transfer.get_feature_names())
    
    if __name__ == '__main__':
        dict_demo()
    

    注意观察没有加上sparse=False参数的结果

      (0, 1)	1.0
      (0, 3)	100.0
      (1, 0)	1.0
      (1, 3)	60.0
      (2, 2)	1.0
      (2, 3)	30.0
    ********************
    ['city=上海', 'city=北京', 'city=深圳', 'temperature']
    

    这个结果并不是我们想要看到的,所以加上参数,得到想要的结果:

    [[  0.   1.   0. 100.]
     [  1.   0.   0.  60.]
     [  0.   0.   1.  30.]]
    ********************
    ['city=上海', 'city=北京', 'city=深圳', 'temperature']
    

    之前在学习pandas中的离散化的时候,也实现了类似的效果。

    我们把这个处理数据的技巧叫做”one-hot“编码:

    在这里插入图片描述

    转化为:

    在这里插入图片描述

    总结

    对于特征当中存在类别信息的我们都会做one-hot编码处理

    文本特征提取

    作用:对文本数据进行特征值化

    • sklearn.feature_extraction.text.CountVectorizer(stop_words=[])
      • 返回词频矩阵
      • CountVectorizer.fit_transform(X)
        • X:文本或者包含文本字符串的可迭代对象
        • 返回值:返回sparse矩阵
      • CountVectorizer.get_feature_names() 返回值:单词列表
    • sklearn.feature_extraction.text.TfidfVectorizer

    应用

    我们对以下数据进行特征提取

    ["life is short,i like python",
    "life is too long,i dislike python"]
    

    在这里插入图片描述

    流程分析

    • 实例化类CountVectorizer
    • 调用fit_transform方法输入数据并转换 (注意返回格式,利用toarray()进行sparse矩阵转换array数组)
    from sklearn.feature_extraction.text import CountVectorizer
    
    def text_count_demo():
        """
        对文本进行特征抽取,countvetorizer
        :return: None
        """
        data = ["life is short,i like like python", "life is too long,i dislike python"]
        # 1、实例化一个转换器类
        # transfer = CountVectorizer(sparse=False) # 注意,没有sparse这个参数
        transfer = CountVectorizer()
        # 2、调用fit_transform
        data = transfer.fit_transform(data)
        print("文本特征抽取的结果:\n", data.toarray())
        print("返回特征名字:\n", transfer.get_feature_names())
    
        return None
    
    if __name__ == '__main__':
        text_count_demo()
    

    返回结果:

    文本特征抽取的结果:
     [[0 1 1 2 0 1 1 0]
     [1 1 1 0 1 1 0 1]]
    返回特征名字:
     ['dislike', 'is', 'life', 'like', 'long', 'python', 'short', 'too']
    

    问题:如果我们将数据替换成中文?

    "人生苦短,我喜欢Python","生活太长久,我不喜欢Python"
    

    那么最终得到的结果是

    在这里插入图片描述

    为什么会得到这样的结果呢,仔细分析之后会发现英文默认是以空格分开的。其实就达到了一个分词的效果,所以我们要对中文进行分词处理

    jieba分词处理
    • jieba.cut()
      • 返回词语组成的生成器

    需要安装下jieba库

    pip3 install jieba
    

    案例分析

    对以下三句话进行特征值化

    今天很残酷,明天更残酷,后天很美好,
    但绝对大部分是死在明天晚上,所以每个人不要放弃今天。
    
    我们看到的从很远星系来的光是在几百万年之前发出的,
    这样当我们看到宇宙时,我们是在看它的过去。
    
    如果只用一种方式了解某样事物,你就不会真正了解它。
    了解事物真正含义的秘密取决于如何将其与我们所了解的事物相联系。
    
    • 分析
      • 准备句子,利用jieba.cut进行分词
      • 实例化CountVectorizer
      • 将分词结果变成字符串当作fit_transform的输入值

    在这里插入图片描述

    from sklearn.feature_extraction.text import CountVectorizer
    import jieba
    
    def cut_word(text):
        """
        对中文进行分词
        "我爱北京天安门"————>"我 爱 北京 天安门"
        :param text:
        :return: text
        """
        # 用结巴对中文字符串进行分词
        text = " ".join(list(jieba.cut(text)))
    
        return text
    
    def text_chinese_count_demo2():
        """
        对中文进行特征抽取
        :return: None
        """
        data = ["一种还是一种今天很残酷,明天更残酷,后天很美好,但绝对大部分是死在明天晚上,所以每个人不要放弃今天。",
                "我们看到的从很远星系来的光是在几百万年之前发出的,这样当我们看到宇宙时,我们是在看它的过去。",
                "如果只用一种方式了解某样事物,你就不会真正了解它。了解事物真正含义的秘密取决于如何将其与我们所了解的事物相联系。"]
        # 将原始数据转换成分好词的形式
        text_list = []
        for sent in data:
            text_list.append(cut_word(sent))
        print(text_list)
    
        # 1、实例化一个转换器类
        # transfer = CountVectorizer(sparse=False)
        transfer = CountVectorizer()
        # 2、调用fit_transform
        data = transfer.fit_transform(text_list)
        print("文本特征抽取的结果:\n", data.toarray())
        print("返回特征名字:\n", transfer.get_feature_names())
    
        return None
    

    返回结果:

    Building prefix dict from the default dictionary ...
    Dumping model to file cache /var/folders/mz/tzf2l3sx4rgg6qpglfb035_r0000gn/T/jieba.cache
    Loading model cost 1.032 seconds.
    ['一种 还是 一种 今天 很 残酷 , 明天 更 残酷 , 后天 很 美好 , 但 绝对 大部分 是 死 在 明天 晚上 , 所以 每个 人 不要 放弃 今天 。', '我们 看到 的 从 很 远 星系 来 的 光是在 几百万年 之前 发出 的 , 这样 当 我们 看到 宇宙 时 , 我们 是 在 看 它 的 过去 。', '如果 只用 一种 方式 了解 某样 事物 , 你 就 不会 真正 了解 它 。 了解 事物 真正 含义 的 秘密 取决于 如何 将 其 与 我们 所 了解 的 事物 相 联系 。']
    Prefix dict has been built succesfully.
    文本特征抽取的结果:
     [[2 0 1 0 0 0 2 0 0 0 0 0 1 0 1 0 0 0 0 1 1 0 2 0 1 0 2 1 0 0 0 1 1 0 0 1 0]
     [0 0 0 1 0 0 0 1 1 1 0 0 0 0 0 0 0 1 3 0 0 0 0 1 0 0 0 0 2 0 0 0 0 0 1 0 1]
     [1 1 0 0 4 3 0 0 0 0 1 1 0 1 0 1 1 0 1 0 0 1 0 0 0 1 0 0 0 2 1 0 0 1 0 0 0]]
    返回特征名字:
     ['一种', '不会', '不要', '之前', '了解', '事物', '今天', '光是在', '几百万年', '发出', '取决于', '只用', '后天', '含义', '大部分', '如何', '如果', '宇宙', '我们', '所以', '放弃', '方式', '明天', '星系', '晚上', '某样', '残酷', '每个', '看到', '真正', '秘密', '绝对', '美好', '联系', '过去', '还是', '这样']
    

    但如果把这样的词语特征用于分类,会出现什么问题?

    请看问题:

    在这里插入图片描述

    该如何处理某个词或短语在多篇文章中出现的次数高这种情况

    Tf-idf文本特征提取
    • TF-IDF的主要思想是:如果某个词或短语在一篇文章中出现的概率高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。
    • TF-IDF作用:用以评估一字词对于一个文件集或一个语料库中的其中一份文件的重要程度。

    公式

    • 词频(term frequency,tf)指的是某一个给定的词语在该文件中出现的频率
    • 逆向文档频率(inverse document frequency,idf)是一个词语普遍重要性的度量。某一特定词语的idf,可以由总文件数目除以包含该词语之文件的数目,再将得到的商取以10为底的对数得到

    在这里插入图片描述

    最终得出结果可以理解为重要程度。

    举例:
    假如一篇文章的总词语数是100个,而词语"非常"出现了5次,那么"非常"一词在该文件中的词频就是5/100=0.05。
    而计算文件频率(IDF)的方法是以文件集的文件总数,除以出现"非常"一词的文件数。
    所以,如果"非常"一词在1,0000份文件出现过,而文件总数是10,000,000份的话,
    其逆向文件频率就是lg(10,000,000 / 1,0000=3。
    最后"非常"对于这篇文档的tf-idf的分数为0.05 * 3=0.15
    

    案例

    from sklearn.feature_extraction.text import TfidfVectorizer
    import jieba
    
    def cut_word(text):
        """
        对中文进行分词
        "我爱北京天安门"————>"我 爱 北京 天安门"
        :param text:
        :return: text
        """
        # 用结巴对中文字符串进行分词
        text = " ".join(list(jieba.cut(text)))
    
        return text
    
    def text_chinese_tfidf_demo():
        """
        对中文进行特征抽取
        :return: None
        """
        data = ["一种还是一种今天很残酷,明天更残酷,后天很美好,但绝对大部分是死在明天晚上,所以每个人不要放弃今天。",
                "我们看到的从很远星系来的光是在几百万年之前发出的,这样当我们看到宇宙时,我们是在看它的过去。",
                "如果只用一种方式了解某样事物,你就不会真正了解它。了解事物真正含义的秘密取决于如何将其与我们所了解的事物相联系。"]
        # 将原始数据转换成分好词的形式
        text_list = []
        for sent in data:
            text_list.append(cut_word(sent))
        print(text_list)
    
        # 1、实例化一个转换器类
        # transfer = CountVectorizer(sparse=False)
        transfer = TfidfVectorizer(stop_words=['一种', '不会', '不要'])
        # 2、调用fit_transform
        data = transfer.fit_transform(text_list)
        print("文本特征抽取的结果:\n", data.toarray())
        print("返回特征名字:\n", transfer.get_feature_names())
    
        return None
    

    返回结果:

    Building prefix dict from the default dictionary ...
    Loading model from cache /var/folders/mz/tzf2l3sx4rgg6qpglfb035_r0000gn/T/jieba.cache
    Loading model cost 0.856 seconds.
    Prefix dict has been built succesfully.
    ['一种 还是 一种 今天 很 残酷 , 明天 更 残酷 , 后天 很 美好 , 但 绝对 大部分 是 死 在 明天 晚上 , 所以 每个 人 不要 放弃 今天 。', '我们 看到 的 从 很 远 星系 来 的 光是在 几百万年 之前 发出 的 , 这样 当 我们 看到 宇宙 时 , 我们 是 在 看 它 的 过去 。', '如果 只用 一种 方式 了解 某样 事物 , 你 就 不会 真正 了解 它 。 了解 事物 真正 含义 的 秘密 取决于 如何 将 其 与 我们 所 了解 的 事物 相 联系 。']
    文本特征抽取的结果:
     [[ 0.          0.          0.          0.43643578  0.          0.          0.
       0.          0.          0.21821789  0.          0.21821789  0.          0.
       0.          0.          0.21821789  0.21821789  0.          0.43643578
       0.          0.21821789  0.          0.43643578  0.21821789  0.          0.
       0.          0.21821789  0.21821789  0.          0.          0.21821789
       0.        ]
     [ 0.2410822   0.          0.          0.          0.2410822   0.2410822
       0.2410822   0.          0.          0.          0.          0.          0.
       0.          0.2410822   0.55004769  0.          0.          0.          0.
       0.2410822   0.          0.          0.          0.          0.48216441
       0.          0.          0.          0.          0.          0.2410822
       0.          0.2410822 ]
     [ 0.          0.644003    0.48300225  0.          0.          0.          0.
       0.16100075  0.16100075  0.          0.16100075  0.          0.16100075
       0.16100075  0.          0.12244522  0.          0.          0.16100075
       0.          0.          0.          0.16100075  0.          0.          0.
       0.3220015   0.16100075  0.          0.          0.16100075  0.          0.
       0.        ]]
    返回特征名字:
     ['之前', '了解', '事物', '今天', '光是在', '几百万年', '发出', '取决于', '只用', '后天', '含义', '大部分', '如何', '如果', '宇宙', '我们', '所以', '放弃', '方式', '明天', '星系', '晚上', '某样', '残酷', '每个', '看到', '真正', '秘密', '绝对', '美好', '联系', '过去', '还是', '这样']
    

    Tf-idf的重要性

    分类机器学习算法进行文章分类中前期数据处理方式

    展开全文
  • 决策树实现ID3、C4.5、CART算法 Author: 浅若清风cyf Date: 2020/12/15 一、创建数据集 手动 def createDataSet(): """ 创建测试的数据集 :return: """ dataSet = [ # 1 ['青绿', '蜷缩', '浊响', '清晰'...

    决策树实现ID3、C4.5、CART算法

    • Author: 浅若清风cyf
    • Date: 2020/12/15

    一、创建数据集

    • 手动
    def createDataSet():
        """
        创建测试的数据集
        :return:
        """
        dataSet = [
            # 1
            ['青绿', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', '好瓜'],
            # 2
            ['乌黑', '蜷缩', '沉闷', '清晰', '凹陷', '硬滑', '好瓜'],
            # 3
            ['乌黑', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', '好瓜'],
            # 4
            ['青绿', '蜷缩', '沉闷', '清晰', '凹陷', '硬滑', '好瓜'],
            # 5
            ['浅白', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', '好瓜'],
            # 6
            ['青绿', '稍蜷', '浊响', '清晰', '稍凹', '软粘', '好瓜'],
            # 7
            ['乌黑', '稍蜷', '浊响', '稍糊', '稍凹', '软粘', '好瓜'],
            # 8
            ['乌黑', '稍蜷', '浊响', '清晰', '稍凹', '硬滑', '好瓜'],
    
            # ----------------------------------------------------
            # 9
            ['乌黑', '稍蜷', '沉闷', '稍糊', '稍凹', '硬滑', '坏瓜'],
            # 10
            ['青绿', '硬挺', '清脆', '清晰', '平坦', '软粘', '坏瓜'],
            # 11
            ['浅白', '硬挺', '清脆', '模糊', '平坦', '硬滑', '坏瓜'],
            # 12
            ['浅白', '蜷缩', '浊响', '模糊', '平坦', '软粘', '坏瓜'],
            # 13
            ['青绿', '稍蜷', '浊响', '稍糊', '凹陷', '硬滑', '坏瓜'],
            # 14
            ['浅白', '稍蜷', '沉闷', '稍糊', '凹陷', '硬滑', '坏瓜'],
            # 15
            ['乌黑', '稍蜷', '浊响', '清晰', '稍凹', '软粘', '坏瓜'],
            # 16
            ['浅白', '蜷缩', '浊响', '模糊', '平坦', '硬滑', '坏瓜'],
            # 17
            ['青绿', '蜷缩', '沉闷', '稍糊', '稍凹', '硬滑', '坏瓜']
        ]
    
        # 特征值列表
        labels = ['色泽', '根蒂', '敲击', '纹理', '脐部', '触感']
    
        # 特征对应的所有可能的情况
        labels_full = {}
    
        for i in range(len(labels)):
            labelList = [example[i] for example in dataSet]
            uniqueLabel = set(labelList)
            labels_full[labels[i]] = uniqueLabel
    
        return dataSet, labels, labels_full
    
    dataSet, labels, labels_full=createDataSet()
    print(dataSet)
    print(labels)
    print(labels_full)
    
    [['青绿', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', '好瓜'], ['乌黑', '蜷缩', '沉闷', '清晰', '凹陷', '硬滑', '好瓜'], ['乌黑', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', '好瓜'], ['青绿', '蜷缩', '沉闷', '清晰', '凹陷', '硬滑', '好瓜'], ['浅白', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', '好瓜'], ['青绿', '稍蜷', '浊响', '清晰', '稍凹', '软粘', '好瓜'], ['乌黑', '稍蜷', '浊响', '稍糊', '稍凹', '软粘', '好瓜'], ['乌黑', '稍蜷', '浊响', '清晰', '稍凹', '硬滑', '好瓜'], ['乌黑', '稍蜷', '沉闷', '稍糊', '稍凹', '硬滑', '坏瓜'], ['青绿', '硬挺', '清脆', '清晰', '平坦', '软粘', '坏瓜'], ['浅白', '硬挺', '清脆', '模糊', '平坦', '硬滑', '坏瓜'], ['浅白', '蜷缩', '浊响', '模糊', '平坦', '软粘', '坏瓜'], ['青绿', '稍蜷', '浊响', '稍糊', '凹陷', '硬滑', '坏瓜'], ['浅白', '稍蜷', '沉闷', '稍糊', '凹陷', '硬滑', '坏瓜'], ['乌黑', '稍蜷', '浊响', '清晰', '稍凹', '软粘', '坏瓜'], ['浅白', '蜷缩', '浊响', '模糊', '平坦', '硬滑', '坏瓜'], ['青绿', '蜷缩', '沉闷', '稍糊', '稍凹', '硬滑', '坏瓜']]
    ['色泽', '根蒂', '敲击', '纹理', '脐部', '触感']
    {'色泽': {'青绿', '乌黑', '浅白'}, '根蒂': {'硬挺', '蜷缩', '稍蜷'}, '敲击': {'浊响', '清脆', '沉闷'}, '纹理': {'稍糊', '清晰', '模糊'}, '脐部': {'凹陷', '稍凹', '平坦'}, '触感': {'软粘', '硬滑'}}
    
    • 从文件读取
    import numpy as np
    import pandas as pd
    # df=pd.read_excel("./watermelon20.xlsx")
    # df.to_csv('./watermelon20.csv',index=False)
    df=pd.read_csv('./watermelon20.csv')
    print(df)
    # 属性集合
    attr=df.columns.values.tolist()[1:]
    data_org=np.array(df[attr])
    # static_attr=df.columns.values.tolist()[1:]#这里的属性 不改变,仅仅作为索引
    print(attr)
    print(len(attr))
    print(data_org.shape)
    print(data_org)
    
    # print(static_attr)
    
    
        编号  色泽  根蒂  敲声  纹理  脐部  触感 好瓜
    0    1  青绿  蜷缩  浊响  清晰  凹陷  硬滑  是
    1    2  乌黑  蜷缩  沉闷  清晰  凹陷  硬滑  是
    2    3  乌黑  蜷缩  浊响  清晰  凹陷  硬滑  是
    3    4  青绿  蜷缩  沉闷  清晰  凹陷  硬滑  是
    4    5  浅白  蜷缩  浊响  清晰  凹陷  硬滑  是
    5    6  青绿  稍蜷  浊响  清晰  稍凹  软粘  是
    6    7  乌黑  稍蜷  浊响  稍糊  稍凹  软粘  是
    7    8  乌黑  稍蜷  浊响  清晰  稍凹  硬滑  是
    8    9  乌黑  稍蜷  沉闷  稍糊  稍凹  硬滑  否
    9   10  青绿  硬挺  清脆  清晰  平坦  软粘  否
    10  11  浅白  硬挺  清脆  模糊  平坦  硬滑  否
    11  12  浅白  蜷缩  浊响  模糊  平坦  软粘  否
    12  13  青绿  稍蜷  浊响  稍糊  凹陷  硬滑  否
    13  14  浅白  稍蜷  沉闷  稍糊  凹陷  硬滑  否
    14  15  乌黑  稍蜷  浊响  清晰  稍凹  软粘  否
    15  16  浅白  蜷缩  浊响  模糊  平坦  硬滑  否
    16  17  青绿  蜷缩  沉闷  稍糊  稍凹  硬滑  否
    ['色泽', '根蒂', '敲声', '纹理', '脐部', '触感', '好瓜']
    7
    (17, 7)
    [['青绿' '蜷缩' '浊响' '清晰' '凹陷' '硬滑' '是']
     ['乌黑' '蜷缩' '沉闷' '清晰' '凹陷' '硬滑' '是']
     ['乌黑' '蜷缩' '浊响' '清晰' '凹陷' '硬滑' '是']
     ['青绿' '蜷缩' '沉闷' '清晰' '凹陷' '硬滑' '是']
     ['浅白' '蜷缩' '浊响' '清晰' '凹陷' '硬滑' '是']
     ['青绿' '稍蜷' '浊响' '清晰' '稍凹' '软粘' '是']
     ['乌黑' '稍蜷' '浊响' '稍糊' '稍凹' '软粘' '是']
     ['乌黑' '稍蜷' '浊响' '清晰' '稍凹' '硬滑' '是']
     ['乌黑' '稍蜷' '沉闷' '稍糊' '稍凹' '硬滑' '否']
     ['青绿' '硬挺' '清脆' '清晰' '平坦' '软粘' '否']
     ['浅白' '硬挺' '清脆' '模糊' '平坦' '硬滑' '否']
     ['浅白' '蜷缩' '浊响' '模糊' '平坦' '软粘' '否']
     ['青绿' '稍蜷' '浊响' '稍糊' '凹陷' '硬滑' '否']
     ['浅白' '稍蜷' '沉闷' '稍糊' '凹陷' '硬滑' '否']
     ['乌黑' '稍蜷' '浊响' '清晰' '稍凹' '软粘' '否']
     ['浅白' '蜷缩' '浊响' '模糊' '平坦' '硬滑' '否']
     ['青绿' '蜷缩' '沉闷' '稍糊' '稍凹' '硬滑' '否']]
    
    • 决策树结构【ID3】
    # 决策树结构:【字典的多重嵌套】
    {
            "纹理": {
                    "稍糊": {
                            "触感": {
                                    "硬滑": "否",
                                    "软粘": "是"
                            }
                    },
                    "清晰": {
                            "根蒂": {
                                    "蜷缩": "是",
                                    "硬挺": "否",
                                    "稍蜷": {
                                            "色泽": {
                                                    "青绿": "是",
                                                    "浅白": "是",
                                                    "乌黑": {
                                                            "触感": {
                                                                    "硬滑": "是",
                                                                    "软粘": "否"
                                                            }
                                                    }
                                            }
                                    }
                            }
                    },
                    "模糊": "否"
            }
    }
    
    • 决策树结构【C4.5】
    {
            "纹理": {
                    "模糊": "否",
                    "稍糊": {
                            "触感": {
                                    "软粘": "是",
                                    "硬滑": "否"
                            }
                    },
                    "清晰": {
                            "触感": {
                                    "软粘": {
                                            "色泽": {
                                                    "乌黑": "否",
                                                    "青绿": {
                                                            "根蒂": {
                                                                    "硬挺": "否",
                                                                    "蜷缩": "是",
                                                                    "稍蜷": "是"
                                                            }
                                                    },
                                                    "浅白": "否"
                                            }
                                    },
                                    "硬滑": "是"
                            }
                    }
            }
    }
    
    • 决策树结构【CART】
    {
            "清晰": {
                    "yes": {
                            "硬滑": {
                                    "yes": "是",
                                    "no": {
                                            "青绿": {
                                                    "yes": {
                                                            "稍蜷": {
                                                                    "yes": "是",
                                                                    "no": "否"
                                                            }
                                                    },
                                                    "no": "否"
                                            }
                                    }
                            }
                    },
                    "no": {
                            "乌黑": {
                                    "yes": {
                                            "浊响": {
                                                    "yes": "是",
                                                    "no": "否"
                                            }
                                    },
                                    "no": "否"
                            }
                    }
            }
    }
    
    • 可视化结果【ID3】
    import matplotlib.pyplot as plt
    import numpy as np
    fig=plt.figure(figsize=(12,8))
    img=plt.imread('./决策树正确结果.jpg')
    plt.imshow(img)
    plt.axis('off')
    plt.show()
    

    在这里插入图片描述

    • 算法伪代码
    fig=plt.figure(figsize=(16,10))
    img=plt.imread('./决策树算法流程.jpg')
    plt.imshow(np.uint8(img))
    plt.axis('off')
    plt.show()
    

    在这里插入图片描述

    • ID3:信息增益
    fig=plt.figure(figsize=(16,12))
    img=plt.imread('./决策树ID3-信息增益.jpg')
    plt.imshow(img)
    plt.axis('off')
    plt.show()
    

    在这里插入图片描述

    • C4.5:增益率
    fig=plt.figure(figsize=(16,14))
    img=plt.imread('./决策树C4.5-增益率.jpg')
    plt.imshow(img)
    plt.axis('off')
    plt.show()
    

    在这里插入图片描述

    • CART:基尼指数
    fig=plt.figure(figsize=(16,12))
    img=plt.imread('./决策树CART-基尼指数.jpg')
    plt.imshow(img)
    plt.axis('off')
    plt.show()
    

    在这里插入图片描述

    • 完整代码
    import numpy as np
    import pandas as pd
    from collections import Counter
    import pprint
    import json
    
    class DecisionTree():
        D = None  # 数据集
        attribute_list = None  # 属性集
        attribute_value_list = dict()  # 属性集对应取值集合
        tree = None  # 决策树【Notice: 字典类型是引用传值,因此需要在init中再初始化它,否则对这个类创建多个对象是该成员变量会指向同一个地址,导致数据会叠加在一起】
    
        def __init__(self):  # 构造函数:自动加载数据集
            self.tree=dict()
            df = pd.read_csv('./watermelon20.csv')
            # 属性集合
            self.attribute_list = df.columns.values.tolist()[1:]
            # 数据集(过滤掉编号)
            self.D = np.array(df[self.attribute_list])
            # 获取每个属性的每个属性值
            for i in range(len(self.attribute_list)):
                self.attribute_value_list[self.attribute_list[i]] = set(df[self.attribute_list[i]])
            # 去除类别
            self.attribute_list = self.attribute_list[:-1]
    
        # 判断集合是否属于同一个类别C【是则设为叶结点,标记为类别C】
        def isSameLabel(self, D):
            labels = [D[i][-1] for i in range(len(D))]  # 取出每个样本的标签
            return len(set(labels)) == 1  # 属于同一个类别则labels集合元素数量为1,返回True
    
        # 判断数据集中的所有属性上的取值是否相同【相同的话设为叶结点,并标记为类别多的类别】
        def isEmptyOrSameAttribute(self, D, attribute_list):
            if len(attribute_list) == 0:
                print("所有属性划分完,无法继续划分,设为叶结点")
                # print("len(attribute_list) == 0")
                return True
            else:
                attribute_index_list = []
                for i in attribute_list:
                    attribute_index_list.append(self.attribute_list.index(i))
                subset_D = D[:, np.array(attribute_index_list)]
                for i in range(1, subset_D.shape[0]):
                    if (subset_D[0] == subset_D[i]).all():
                        pass
                    else:
                        return False
            print("所有样本的所有属性相同,无法划分")
            return True
    
        # 计算信息熵
        def Ent(self, D):
            labels = D[:, -1]
            count_result = Counter(labels)
            # 统计每个标签的频数
            labels_count = np.array(list(count_result.values()))
            p = labels_count / D.shape[0]
            # 计算信息熵
            ent = -1 * np.sum(p * np.log2(p))
            return ent
    
        # 计算信息增益
        def Gain(self, D, attribute):
            # 统计属性attribute的每个取值的样本数
            attribute_values = np.squeeze(D[:, self.attribute_list.index(attribute)])  # 获取每个样本在属性attribute上的取值
            attribute_keys = np.array(list(set(list(attribute_values))))  # 获取所有属性值
            D_split = []
            for i in range(attribute_keys.shape[0]):
                mask = (attribute_values == attribute_keys[i])
                D_split.append(D[mask])  # 按照属性 attribute每个取值划分数据集
            D_split = np.array(D_split)
            # 计算每个属性值的信息熵
            ent_list = []
            attribute_i_count_list = []
            for i in range(D_split.shape[0]):
                ent_list.append(self.Ent(D_split[i]))
                attribute_i_count_list.append(D_split[i].shape[0])
            ent_list = np.array(ent_list)
            attribute_i_count_list = np.array(attribute_i_count_list)
            # 计算信息增益
            gain = self.Ent(D) - np.sum(attribute_i_count_list / D.shape[0] * ent_list)
            return gain
    
        # 计算增益率
        def Gain_ratio(self, D, attribute):
            D_attribute_values = np.squeeze(D[:, self.attribute_list.index(attribute)])  # 获取每个样本在属性attribute上的取值
            count_result=Counter(D_attribute_values)
            attribute_i_count_list=np.array(list(count_result.values()))
            IV=-1*np.sum(attribute_i_count_list/D.shape[0]*np.log2(attribute_i_count_list/D.shape[0]))
            gain_ratio=self.Gain(D,attribute)/IV
            return gain_ratio
    
        # 计算基尼值【数据集D的不纯度】
        def Gini(self,D):
            # 获取集合D的标签
            D_labels=D[:, -1]
            count_result = Counter(D_labels)
            # 统计每个标签的频数
            labels_count = np.array(list(count_result.values()))
            p = labels_count / D.shape[0]
            return 1-np.sum(p*p)
        
        # 计算基尼指数【计算属性attribute中按照某个属性划分得到的两个集合(二叉树)的基尼系数最小的作为划分属性】
        def Gini_index(self,D,attribute):
            # 获取样本集D在属性attribute上的取值
            D_attribute_values = np.squeeze(D[:, self.attribute_list.index(attribute)])  # 获取每个样本在属性attribute上的取值
            # 统计每个属性值的样本数【字典】
            count_result=Counter(D_attribute_values)
            # 统计属性的所有取值【转换成数组】
            attribute_keys=np.array(list(count_result.keys()))
    #         attribute_values_count_list=np.array(list(count_result.values()))
            # 按照不同属性值划分数据集【是/否】【CART算法是划分为二叉树,而不是多叉树】
            gini_index_list=[]
            for i in range(attribute_keys.shape[0]):
                D_split=[]
                D_split_count=[]
                mask = (D_attribute_values == attribute_keys[i])
                D_split.append(D[mask])  # 取值与属性值相同:是
                D_split.append(D[(1-mask).astype('bool')]) 
                D_split = np.array(D_split)
                D_split_count.append(D_split[0].shape[0])
                D_split_count.append(D_split[1].shape[0])
                D_split_count=np.array(D_split_count)
                # 计算按照该属性值划分后的Gini值
                gini_list=[]
                for i in range(D_split.shape[0]):
                    gini_list.append(self.Gini(D_split[i]))
                gini_list = np.array(gini_list)
                # 计算基尼指数
                gini_index = np.sum(D_split_count / D.shape[0] * gini_list)  # D.shape[0]==2
                gini_index_list.append(gini_index)
            # 选择最小的基尼指数作为属性attribute的基尼指数
            gini_index_list=np.array(gini_index_list)
            gini_index_min=np.min(gini_index_list)
            gini_index_min_attribute_value=attribute_keys[np.argmin(gini_index_list)]
            return gini_index_min,gini_index_min_attribute_value
            
    
        # 计算最优划分属性
        def get_bestAttribute(self, D, attribute_list, alg='ID3'):
            '''
            Notice: ID3和C4.5算法执行次函数有一个返回值,而CART算法有两个返回值
            '''
            if alg == 'ID3':
                best = attribute_list[0]
                max_gain = 0
                for i in attribute_list:
                    gain_i = self.Gain(D, i)
                    if gain_i > max_gain:
                        best = i
                        max_gain = gain_i
                # print('best=', best, 'max_gain=', max_gain)
                return best
            elif alg == 'C4.5':
                # 增益率准则对可取值数目较少的属性有所偏好,C4.5算法并不是直接选择增益率最大的候选划分属性,
                # 而是使用一个启发式:先从候选划分属性中找出信息增益高于平均水平的属性,再从中选择增益率最高的
                gain_list=[]
                for i in attribute_list:
                    gain_list.append(self.Gain(D,i))
                gain_list=np.array(gain_list)
                gain_mean=np.mean(gain_list)
                attribute_chosen=np.array(attribute_list)[gain_list>=gain_mean]  # 注意要加=,当只有一个属性值或者所有属性增益率相同时,没有属性的增益率大于平均值
                gain_rate_list=[]
                for i in attribute_chosen:
                    gain_rate_list.append(self.Gain_ratio(D,i))
                gain_rate_list=np.array(gain_rate_list)
                best = attribute_chosen[np.argmax(gain_rate_list)]
                return best
            elif alg=='CART':
                # 基尼值Gini(D)反映了从数据集D中随机抽取两个样本,其类别标记不一致的概率,因此,Gini(D)值越小,数据集D的纯度越高
                # 在属性集中选择划分后基尼指数最小的属性作为最优属性
                gini_index_list=[]
                gini_index_attribute_value_list=[]
                for i in attribute_list:
                    gini_index_min,gini_index_min_attribute_value=self.Gini_index(D,i)
                    gini_index_list.append(gini_index_min)
                    gini_index_attribute_value_list.append(gini_index_min_attribute_value)
                gini_index_list=np.array(gini_index_list)
                gini_index_attribute_value_list=np.array(gini_index_attribute_value_list)
                
                best_attribute_idx=np.argmin(gini_index_list)
                return attribute_list[best_attribute_idx],gini_index_attribute_value_list[best_attribute_idx]
                
            else:
                raise Exception("请选择合法的划分属性选优算法!")
    
        # 构建决策树tree【这里树结构采用嵌套的字典类型】
        def createTree(self, tree, D, attributes,alg='ID3'):
            attribute_list = attributes.copy()
            # 判断数据集是否属于同一个类别【不用再划分】
            if self.isSameLabel(D):
                return D[0][-1]
    
            if self.isEmptyOrSameAttribute(D, attribute_list):
                # 获取样本数多的类
                labels = D[:, -1]  # 获取所有样本的标签
                labels_set = set(list(np.squeeze(labels)))  # 获取标签集合
                labels_dict = dict()  # 获取每个标签对应的样本
                for i in labels_set:  # 初始化
                    labels_dict[i] = 0
                for i in range(D.shape[0]):  # 统计每个标签的样本数
                    labels_dict[D[i][-1]] += 1
                keys = list(labels_dict.keys())
                values = list(labels_dict.values())
                return keys[np.argmax(values)]
    
            if alg=='ID3' or alg=='C4.5':
                # 选择最优划分属性【选择后需要在属性集中取出该属性再进行递归】
                best_attribute = self.get_bestAttribute(D, attribute_list, alg=alg)
    
                # 属性集取出最优属性,进行下一轮递归
                attribute_list.remove(best_attribute)
                # 获取数据集在最优属性上的所有取值
                attribute_values = self.attribute_value_list[best_attribute] 
    
                # 按照最优属性的每个值划分数据集
                D_attribute_values = np.squeeze(D[:, self.attribute_list.index(best_attribute)])  # 获取每个样本在属性attribute上的取值
                D_split = dict()
                # 按每个取值划分数据集
                for i in attribute_values:
                    mask = (D_attribute_values == i)
                    D_split[i] = D[mask]  # 按照属性 attribute每个取值划分数据集
    
                # 对最优属性的每个取值进行遍历
                subTree = dict()
    
                tree[best_attribute] = dict()
                for i in attribute_values:
                    if D_split[i].shape[0] == 0:  # 该属性上没有样本,根据父结点的样本分布作为当前结点的样本分布
                        labels=D[:,-1]
                        result=Counter(labels)
                        result_keys=list(result.keys())
                        result_values=list(result.values())
                        label=result_keys[np.argmax(result_values)]
                        subTree[i]=label
                        continue
                    subTree[i] = self.createTree(tree[best_attribute], D_split[i], attribute_list,alg=alg)
                
                tree[best_attribute] = subTree
                node=dict()    # 需要单独创建一个结点,而不能直接返回subTree或tree,会导致子节点为None
                node[best_attribute]=subTree
                return node  # 当某个属性值还需划分时,返回子树,否则该属性值的value为None
            elif alg=='CART':
                # 选择最优划分属性和最优属性值【CART算法与ID3和C4.5不同,CART算法使用属性值按是否相等划分成二叉树】
                best_attribute,best_attribute_value = self.get_bestAttribute(D, attribute_list, alg=alg)
    
                # CART算法的属性可以重复使用
    #             attribute_list.remove(best_attribute)
                # 获取数据集在最优属性上的所有取值
    #             attribute_values = self.attribute_value_list[best_attribute] 
    
                # 按照最优属性值划分成两个子数据集
                D_attribute_values = np.squeeze(D[:, self.attribute_list.index(best_attribute)])  # 获取每个样本在属性attribute上的取值
                D_split = dict()
                # 按照最优属性值划分数据集
                mask = (D_attribute_values == best_attribute_value)
                D_split['yes'] = D[mask]  
                D_split['no'] = D[(1-mask).astype('bool')]
    
                # 对最优属性的每个取值进行遍历
                subTree = dict()
    
                tree[best_attribute_value] = dict()
                attribute_values=['yes','no']
            
                for i in attribute_values:                    
                    subTree[i] = self.createTree(tree[best_attribute_value], D_split[i], attribute_list,alg=alg)
                    
                tree[best_attribute_value] = subTree
    
                node=dict()    # 需要单独创建一个结点,而不能直接返回subTree或tree,会导致子节点为None
                node[best_attribute_value]=subTree
                return node  # 当某个属性值还需划分时,返回子树,否则该属性值的value为None
    
        # 构建决策树
        def build(self,alg='ID3'):
            self.createTree(self.tree, self.D, self.attribute_list,alg=alg)
    
        # 可视化决策树【递归输出】
        def show(self,tree,blank):
            if type(tree)!=type(self.tree):
                return
            keys=list(tree.keys())
            for i in keys:
                for t in range(blank):
                    print('\t', end='')
                print('{',i,':')
                self.show(tree[i],blank+1)
                if type(tree[i])!=type(self.tree):  # 是否为叶结点
                    for t in range(blank + 1):
                        print('\t', end='')
                    print(tree[i])
                for t in range(blank):
                    print('\t', end='')
                print('}')
                
        # 可视化决策树【调包pprint】
        def showTreeDict(self):
            pprint.pprint(self.tree)
    
        # 可视化决策树【调包json】    
        def showTreeDictJson(self):
            js=json.dumps(self.tree,indent=8,ensure_ascii=False)
            print(js)
        
        # 使用ID3/C4.5生成的决策树进行判断
        def decision(self,sample):
            print("输入样本:",sample)
            attribute=list(self.tree.keys())[0]  # '纹理'
            tree=self.tree
            while True:
                if type(tree)==type(self.tree):
                    tree = tree[attribute]
                    tree=tree[sample[self.attribute_list.index(attribute)]]
                    if type(tree)==type(self.tree):
                        attribute=list(tree.keys())[0]
                else:
                    print("识别结果:",end='')
                    print('好瓜') if tree=='是' else print("坏瓜")
                    break
        
        # 使用CART生成的决策树进行判断
        def decision_CART(self,sample):
            print("输入样本:",sample)
            attribute=list(self.tree.keys())[0]  # '纹理'
            tree=self.tree
            while True:
                if type(tree)==type(self.tree):
                    # 获取树的key
                    attribute_value=list(tree.keys())[0]
                    # 检索对应的属性
                    attribute_idx=-1
                    attribute_value_set=set()
                    attribute_value_set.add(attribute_value)
                    for i in self.attribute_list:
                        if attribute_value_set.issubset(self.attribute_value_list[i]):
                            attribute_idx=self.attribute_list.index(i)
                            print(i)
                            break
                    if attribute_idx==-1:
                        raise Exception("Can't find the attribute of {}".format(attribute_value))
                    # 判断样本该属性值是否与决策树的属性值相等
                    attribute_value_equal=(attribute_value==sample[attribute_idx])
                    tree=tree[attribute_value]
                    if attribute_value_equal:
                        tree=tree['yes']
                    else:
                        tree=tree['no']
                else:
                    print("识别结果:",end='')
                    print('好瓜') if tree=='是' else print("坏瓜")
                    break
                    
    
    dt=DecisionTree()
    dt.build(alg='ID3')
    dt.showTreeDictJson()
    dt.decision(dt.D[0][:-1])
    
    {
            "纹理": {
                    "模糊": "否",
                    "稍糊": {
                            "触感": {
                                    "软粘": "是",
                                    "硬滑": "否"
                            }
                    },
                    "清晰": {
                            "根蒂": {
                                    "硬挺": "否",
                                    "蜷缩": "是",
                                    "稍蜷": {
                                            "色泽": {
                                                    "乌黑": {
                                                            "触感": {
                                                                    "软粘": "否",
                                                                    "硬滑": "是"
                                                            }
                                                    },
                                                    "青绿": "是",
                                                    "浅白": "是"
                                            }
                                    }
                            }
                    }
            }
    }
    输入样本: ['青绿' '蜷缩' '浊响' '清晰' '凹陷' '硬滑']
    识别结果:好瓜
    
    dt=DecisionTree()
    dt.build(alg='C4.5')
    dt.showTreeDictJson()
    dt.decision(dt.D[0][:-1])
    
    {
            "纹理": {
                    "模糊": "否",
                    "稍糊": {
                            "触感": {
                                    "软粘": "是",
                                    "硬滑": "否"
                            }
                    },
                    "清晰": {
                            "触感": {
                                    "软粘": {
                                            "色泽": {
                                                    "乌黑": "否",
                                                    "青绿": {
                                                            "根蒂": {
                                                                    "硬挺": "否",
                                                                    "蜷缩": "是",
                                                                    "稍蜷": "是"
                                                            }
                                                    },
                                                    "浅白": "否"
                                            }
                                    },
                                    "硬滑": "是"
                            }
                    }
            }
    }
    输入样本: ['青绿' '蜷缩' '浊响' '清晰' '凹陷' '硬滑']
    识别结果:好瓜
    
    dt=DecisionTree()
    dt.build(alg='CART')
    # pprint.pprint(dt.tree)
    # dt.show(dt.tree,0)
    dt.showTreeDictJson()
    dt.decision_CART(dt.D[0][:-1])
    
    {
            "清晰": {
                    "yes": {
                            "硬滑": {
                                    "yes": "是",
                                    "no": {
                                            "青绿": {
                                                    "yes": {
                                                            "稍蜷": {
                                                                    "yes": "是",
                                                                    "no": "否"
                                                            }
                                                    },
                                                    "no": "否"
                                            }
                                    }
                            }
                    },
                    "no": {
                            "乌黑": {
                                    "yes": {
                                            "浊响": {
                                                    "yes": "是",
                                                    "no": "否"
                                            }
                                    },
                                    "no": "否"
                            }
                    }
            }
    }
    输入样本: ['青绿' '蜷缩' '浊响' '清晰' '凹陷' '硬滑']
    纹理
    触感
    识别结果:好瓜
    

    谨以此纪念《数据挖掘与机器学习》课程期末考试手算ID3决策树!o(╥﹏╥)o ——2021.1.21

    展开全文
  • 目录一、决策树算法基础理论决策树的学习过程ID3算法二、实现针对西瓜数据集的ID3算法实现代码参考文章 一、决策树算法 决策树是一种基于树结构来进行决策的分类算法,我们希望从给定的训练数据集学得一个模型(即...
  • 文章目录一、决策树介绍二、...最经典的决策树算法有ID3、C4.5、CART,其中ID3算法是最早被提出的,它可以处理离散属性样本的分类,C4.5和CART算法则可以处理更加复杂的分类问题。使用算法ID3, C4.5和C5.0生成树算法使
  • 摘要:益来选哪个属性来作为枝干嘛,现在我们用增益率来选!...这就很麻烦了,那么有没有一种更加公正客观的决策树算法呢?有的!!CART决策树上面说到,ID3决策树用信息增益作为属性选取,C4.5用增...
  • 决策树算法的 MATLAB 实践

    千次阅读 2021-02-11 15:04:04
    决策树算法原理: 决策树算法的基本原理 决策树算法是一种特别简单的机器学习分类算法。在机器学习中,决策树是一个预测模型,其代表的是对象属性与对象之间的一种映射关系。 决策树算法的特点: 决策树算法的...
  • 本实验通过鸢尾花数据集iris.csv来实现对决策树进一步的了解。其中, Iris鸢尾花数据集是一个经典数据集,在统计学习和机器学习领域都经常被用作示例。数据集内包含3类共150条记录,每类各50个数据,每条记录都有4项...
  • 使用决策树算法进行鸢尾花数据分类(学习笔记) 决策树算法介绍 构建树的过程 从根节点开始,计算所有特征值的信息增益(信息增益比、基尼系数),选择计算结果最大的特征作为根节点。(信息熵增益->ID3,...
  • ∙\bullet∙ 基本原理:ID3决策树算法使用信息增益来构建决策树,对于所有的属性我们先选择信息增益最大的作为根节点,然后计算其他属性的信息增益再选择最大的作为子节点,一直递归调用该操作,直到信息增益很小...
  • 决策树算法是机器学习中的经典算法1.决策树(decision tree)决策树是一种树形结构,其中每个内部节点表示一个属性上的测试,每个分支代表一个测试输出,每个叶节点代表一种类别。假设小明去看电影,影响看电影的外部...
  • 决策树算法与python——心脏病预测

    千次阅读 2021-10-20 17:33:36
    关于决策树算法: 决策树(Decision Tree)是在已知各种情况发生概率的基础上,通过构成决策树来求取净现值的期望值大于等于零的概率,评价项目风险,判断其可行性的决策分析方法,是直观运用概率分析的一种图解法。...
  • 实验三决策树算法实验一、实验目的:熟悉和掌握决策树的分类原理、实质和过程;掌握典型的学习算法和实现技术。二、实验原理: 决策树学习和分类.三、实验条件:四、实验内容:1 根据现实生活中的原型自己创建一个...
  • 本文实例讲述了java实现的决策树算法。分享给大家供大家参考,具体如下:决策树算法是一种逼近离散函数值的方法。它是一种典型的分类方法,首先对数据进行处理,利用归纳算法生成可读的规则和决策树,然后使用决策对...
  • 基于决策树算法实现电信用户流失预测任务
  • 本文不涉及决策树算法的原理,只通过python代码实现算法,并且没有用到机器学习库,根据算法流程一步一步实现。 决策树 本文使用ID3算法实现决策树。 计算熵 首先是计算香农熵,需要了解计算公式 # 计算数据集的香农...
  • 决策树算法案例:泰坦尼克号乘客生存预测 1 泰坦尼克号数据 在泰坦尼克号和titanic2数据帧描述泰坦尼克号上的个别乘客的生存状态。这里使用的数据集是由各种研究人员开始的。其中包括许多研究人员创建的旅客名单,...
  • 决策树是常用的分类学习算法,当然也能用于处理回归问题,同时也适合集成学习比如随机森林,作为机器学习的入门算法今天简单介绍一下决策树算法的原理和实现(python)决策树的特点:优点决策树易于理解和实现。...
  • 决策树算法属性筛选度量标准 文章目录决策树算法属性筛选度量标准信息熵信息增益信息增益率基尼系数参考资料 在信息论中,熵(entropy)是接收的每条消息中包含的信息的平均量,又被称为信息熵、信源熵、平均自信息...
  • 决策树算法是一种逼近离散函数值的方法。 它是一种典型的 分类方法 ,首先对数据进行处理,利用归纳算法生成可读的规则和决策树,然后使用决策对新数据进行分析。 本质上决策树是通过一系列规则对数据进行分类的过程...
  • 1 什么是决策树 决策树(Decision Tree)是一种基本的分类与回归方法,本文主要讨论分类决策树决策树模型呈树形结构,在分类问题中,表示基于特征对数据进行分类的过程。它可以认为是if-then规则的集合。每个内部...
  • 1.决策树算法决策树(decision tree):决策树类似于流程图的一个树形结构,如图1。其中每个内部结点表示在一个属性上的测试,每个分支代表一个属性的输出,每个叶子结点代表类或者类的分布。树的最顶层是根结点。一个...
  • 《matlab实现的C45分类决策树算法》由会员分享,可在线阅读,更多相关《matlab实现的C45分类决策树算法(6页珍藏版)》请在人人文库网上搜索。1、functionD = C4_5(train_features, train_targets,inc_node, region) %...
  • python 决策树算法之讲解实操(下) 序 在上篇的文章决策树算法之讲解实操(上)当中,我们主要了解了决策树的算法原理,实际应用,以及简单介绍了下决策树的复杂度参数。而这篇我们主要讲解决策树的分析可视化,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 112,351
精华内容 44,940
关键字:

决策树算法