机器学习期末复习 决策树ID3的计算与构建

news2024/11/25 11:32:40

ID3构建的流程就是参考书上的那个伪代码。

1) 开始:构建根节点,将所有训练数据都放在根节点,选择一个最优特征,按着这一特征将训练数据集分割成子集,使得各个子集有一个在当前条件下最好的分类。

2) 如果这些子集已经能够被基本正确分类,那么构建叶节点,并将这些子集分到所对应的叶节点去。

3)如果还有子集不能够被正确的分类,那么就对这些子集选择新的最优特征,继续对其进行分割,构建相应的节点,如果递归进行,直至所有训练数据子集被基本正确的分类,或者没有合适的特征为止。

4)每个子集都被分到叶节点上,即都有了明确的类,这样就生成了一颗决策树。

过程就是建立一棵树(不是二叉树,因为一个属性可能有多个取值),建树的过程我选择的递归。

首先创建一个空结点,然后通过计算信息增益,选取所有属性中信息增益最大的作为这个结点的值;

然后根据这个属性的不同取值来对这个结点进行“分杈”,如此循环往复,下一次循环在计算信息熵时要去掉这次选到的最大信息增益的那部分训练集;

结束的条件是:

(1)当剩下的所有训练集中都属于同一类,比如都是好瓜,那么就没必要再划分了,根节点就是好瓜。

(2)只剩下一个属性了,比如色泽,分出来青绿和浅白,青绿不能继续划分了,那么统计青绿这一部分的训练集的类别,取最大的。比如青绿中有6个样本,4个为好瓜,2个为坏瓜,那么最后是好瓜的概率大,选好瓜作为根节点。

信息熵与信息增益的计算可以参考这个:

【机器学习实战】3、决策树

信息熵:

i的取值范围取决于训练集中给出的类别的数量,比如西瓜训练集,就只有好坏瓜之分,那么i的取值范围就是1-2。

p(x)就是取好瓜(坏瓜)的概率

代码:

import json
import numpy as np
import cv2
import math

# 首先读入一遍数据,找出y有几个,算出信息熵。(暂定使用列表保存,用in来判断是否出现过,从而计数)
# 由y有几个,创建map,来计算信息增益
# 有四个属性,从6-15行,再次遍历一遍,统计每个属性下的正反例的比例
"""
    利用打网球数据集PlayTenis构建决策树,该数据集的特性如下:
    属性包括天气(outlook)、温度(temperature)、湿度(humidity)、是否有风(windy),样本个数为14。
    标签为今天是否去打网球(play)。
    具体数据如下:
    NO. Outlook temperature humidity   windy  play
    1   sunny      hot       high      FALSE  no
    2   sunny      hot       high      TRUE   no
    3   overcast   hot       high      FALSE  yes
    4   rainy      mild      high      FALSE  yes
    5   rainy      cool      normal    FALSE  yes
    6   rainy      cool      normal    TRUE   no
    7   overcast   cool      normal    TRUE   yes
    8   sunny      mild      high      FALSE  no
    9   sunny      cool      normal    FALSE  yes
    10  rainy      mild      normal    FALSE  yes
"""


# 读入数据函数  返回正例,反例,总例,数据
def readFile():
    f = open('D:\\PythonProject_Class\\test_Data\\PlayTennis.txt', 'r')
    lk = 6
    preData = [[] for i in range(lk)]
    dict_PlusFeatures = {}  # 保存属性的名称,并为求信息增益做准备,也就是把初值赋值为0
    dict_NegativeFeatures = {}  # 上一个保存的是正例,这个保存的的是反例
    sum_Features = {}

    for i in range(0, 4):  # 把前几行的文字描述跳过
        s = f.readline()

    s = f.readline()  # 读入属性
    #    NO. Outlook temperature humidity   windy  play
    # strip函数是去除这行的开头和结尾的换行符和空格的
    s = s.strip('\n')
    s = s.strip(' ')
    x = s.split(' ')
    # 初始化字典
    for i in range(1, len(x)):  # 从1开始是要跳过NO.
        if x[i] == 'play':
            dict_PlusFeatures[x[i]] = 0
            dict_NegativeFeatures[x[i]] = 0
            sum_Features[x[i]] = 0
        elif x[i] != '':
            dict_PlusFeatures[x[i]] = {}
            dict_NegativeFeatures[x[i]] = {}
            sum_Features[x[i]] = {}

    ls = [i for i in dict_PlusFeatures.keys()]  # 提取字典中的特征名称
    ls.pop(len(ls) - 1)  # 去掉play
    # s=set()不能kidls=[s for i in range(len(ls))],这样列表中的一个集合改变,其他的也会改变
    # kidls = [set() for i in range(len(ls))]  # 保存每个特征的属性值,使用没有重复元素的集合set

    flag = 0  # 用于标记是正例还是反例
    index = 0  # 用于指向 保存所有读入数据的predata 的下标
    for i in range(lk):
        cnt = 0
        s = f.readline()  # 读入属性
        s = s.strip('\n')
        s = s.strip(' ')
        x = s.split(' ')
        if x[len(x) - 1] == 'no':  # 首先处理是正例还是反例,同时统计正反例个数
            flag = -1
            dict_NegativeFeatures['play'] += 1
        elif x[len(x) - 1] == 'yes':
            flag = 1
            dict_PlusFeatures['play'] += 1
        sum_Features['play'] += 1
        for j in range(2, len(x) - 1):  # 跳过编号以及最后的正反例
            if x[j] != '':
                if flag == 1:
                    if x[j] not in dict_PlusFeatures[ls[cnt]].keys():
                        dict_PlusFeatures[ls[cnt]][x[j]] = 1
                    else:
                        dict_PlusFeatures[ls[cnt]][x[j]] += 1
                elif flag == -1:
                    if x[j] not in dict_NegativeFeatures[ls[cnt]].keys():
                        dict_NegativeFeatures[ls[cnt]][x[j]] = 1
                    else:
                        dict_NegativeFeatures[ls[cnt]][x[j]] += 1

                if x[j] not in sum_Features[ls[cnt]].keys():
                    sum_Features[ls[cnt]][x[j]] = 1
                else:
                    sum_Features[ls[cnt]][x[j]] += 1

                # kidls[cnt].add(x[j])
                preData[index].append(x[j])
                cnt += 1
        preData[index].append(x[len(x) - 1])
        index += 1

    for i in dict_PlusFeatures.keys():
        if i != 'play':
            for j in dict_PlusFeatures[i].keys():
                if j not in dict_NegativeFeatures[i].keys():
                    dict_NegativeFeatures[i][j] = 0

    for i in dict_NegativeFeatures.keys():
        if i != 'play':
            for j in dict_NegativeFeatures[i].keys():
                if j not in dict_PlusFeatures[i].keys():
                    dict_PlusFeatures[i][j] = 0

    preData.insert(0, ls)  # 在split中发现需要表头
    preData[0].append('play')
    return dict_PlusFeatures, dict_NegativeFeatures, sum_Features, preData


# 当样本中只剩下一个属性的时候,返回出现次数最多的结果。
def Maxmajority(classList):
    Cntyes = 0
    Cntno = 0
    for i in classList:
        if i == 'yes':
            Cntyes += 1
        else:
            Cntno += 1
    return max(Cntyes, Cntno)


def getmax(dict_PlusFeatures, dict_NegativeFeatures):
    # print(dict_PlusFeatures)
    # print(dict_NegativeFeatures)
    if dict_PlusFeatures['play'] >= dict_NegativeFeatures['play']:
        return 'yes'
    else:
        return 'no'


def getEnt(dict_PlusFeatures, dict_NegativeFeatures, sum_Features):
    # 计算信息熵
    cnt_samples = sum_Features['play']
    e1 = dict_PlusFeatures['play'] / cnt_samples
    e2 = dict_NegativeFeatures['play'] / cnt_samples
    Ent = -(e1 * math.log(e1, 2) + e2 * math.log(e2, 2))
    # print(Ent)
    return Ent


def getGain(Ent, sum_Features):
    # 计算信息增益
    max = 0
    maxFeature = ""
    for i in sum_Features.keys():
        if i != 'play':
            Gain = 0
            for j in sum_Features[i].keys():
                if dict_PlusFeatures[i][j] == 0:
                    k1 = 0
                else:
                    k1 = dict_PlusFeatures[i][j] / sum_Features[i][j]
                if dict_NegativeFeatures[i][j] == 0:
                    k2 = 0
                else:
                    k2 = dict_NegativeFeatures[i][j] / sum_Features[i][j]
                if k1 == 0 and k2 != 0:
                    ke = k2 * math.log(k2, 2)
                elif k1 != 0 and k2 == 0:
                    ke = k1 * math.log(k1, 2)
                elif k1 == 0 and k2 == 0:
                    ke = 0
                else:
                    ke = k1 * math.log(k1, 2) + k2 * math.log(k2, 2)
                Gain += -(sum_Features[i][j] / sum_Features['play']) * ke
            Gain = Ent - Gain
            if max < Gain:
                max = Gain
                maxFeature = i
    return max, maxFeature


def chooseBestFeature(dict_PlusFeatures, dict_NegativeFeatures, sum_Features):
    Ent = getEnt(dict_PlusFeatures, dict_NegativeFeatures, sum_Features)
    Gain, Name = getGain(Ent, sum_Features)
    print("Gain:{}".format(Gain))
    return Name


# 划分函数
def splitValues(dict_PlusFeatures, dict_NegativeFeatures, sum_Features, preData, BestFeatureName, Value):
    Newdict_PlusFeatures = dict(dict_PlusFeatures)
    Newdict_NegativeFeatures = dict(dict_NegativeFeatures)
    Newsum_Features = dict(sum_Features)

    Newdict_PlusFeatures.pop(BestFeatureName)
    Newdict_PlusFeatures['play'] = 0
    Newdict_NegativeFeatures.pop(BestFeatureName)
    Newdict_NegativeFeatures['play'] = 0
    Newsum_Features.pop(BestFeatureName)
    Newsum_Features['play'] = 0

    for i in Newdict_PlusFeatures.keys():
        if i != 'play':
            for j in Newdict_PlusFeatures[i].keys():
                Newdict_PlusFeatures[i][j] = 0
    for i in Newdict_NegativeFeatures.keys():
        if i != 'play':
            for j in Newdict_NegativeFeatures[i].keys():
                Newdict_NegativeFeatures[i][j] = 0
    for i in Newsum_Features.keys():
        if i != 'play':
            for j in Newsum_Features[i].keys():
                Newsum_Features[i][j] = 0
    BestIndex = 0
    for i in range(len(preData[0])):
        if preData[0][i] == BestFeatureName:
            BestIndex = i
            break
    for i in range(1, len(preData)):
        if preData[i][BestIndex] == Value:
            if preData[i][-1] == 'no':
                Newdict_NegativeFeatures['play'] += 1
                Newsum_Features['play'] += 1
                for j in range(len(preData[i]) - 1):
                    if j != BestIndex:
                        Newdict_NegativeFeatures[preData[0][j]][preData[i][j]] += 1
                        Newsum_Features[preData[0][j]][preData[i][j]] += 1
            elif preData[i][-1] == 'yes':
                Newdict_PlusFeatures['play'] += 1
                Newsum_Features['play'] += 1
                for j in range(len(preData[i]) - 1):
                    if j != BestIndex:
                        Newdict_PlusFeatures[preData[0][j]][preData[i][j]] += 1
                        Newsum_Features[preData[0][j]][preData[i][j]] += 1

    return Newdict_PlusFeatures, Newdict_NegativeFeatures, Newsum_Features


# {'Outlook': {'rainy': {'windy': {'FALSE': {'humidity': {'high': 'no', 'normal': 'yes'}}, 'TRUE': {'temperature': {'cool': 'yes', 'hot': 'no', 'mild': 'yes'}}}},
#              'overcast': 'yes',
#              'sunny': {'temperature': {'cool': {'windy': {'FALSE': 'yes', 'TRUE': 'no'}}, 'hot': {'windy': {'FALSE': 'yes', 'TRUE': 'no'}}, 'mild': {'humidity': {'high': 'no', 'normal': 'yes'}}}}}}
def creatTree(dict_PlusFeatures, dict_NegativeFeatures, sum_Features, preData):
    if dict_PlusFeatures['play'] == 0:  # 如果只有负例的话,就不用分了
        return 'no'
    if dict_NegativeFeatures['play'] == 0:  # 如果只有正例的话,就不用分了
        return 'yes'
    if len(sum_Features) - 1 == 1:  # 如果只剩下一个属性,那么返回最多的
        return getmax(dict_PlusFeatures, dict_NegativeFeatures)
    BestFeatureName = chooseBestFeature(dict_PlusFeatures, dict_NegativeFeatures, sum_Features)  # 计算信息增益,选出最优属性值
    # print(BestFeatureName)
    Tree = {BestFeatureName: {}}  # 建立树

    Values = set()  # 保存的的是最优属性的不同取值,因为要根据这些不同取值对数进行分叉
    for i in sum_Features[BestFeatureName].keys():
        Values.add(i)  # set用add方法

    for i in Values:
        # 选出不同取值的划分
        Newdict_PlusFeatures, Newdict_NegativeFeatures, Newsum_Features = splitValues(dict_PlusFeatures,
                                                                                      dict_NegativeFeatures,
                                                                                      sum_Features,
                                                                                      preData, BestFeatureName, i)

        NewpreData = []
        index = 0
        for j in range(len(preData[0])):
            if preData[0][j] == BestFeatureName:
                index = j
                temp = preData[0][:j] + preData[0][j + 1:]
                NewpreData.append(temp)
                break
        for j in range(len(preData)):
            if preData[j][index] == i:
                temp2 = preData[j][:index] + preData[j][index + 1:]
                NewpreData.append(temp2)
        # print(NewpreData)
        # 递归调用
        Tree[BestFeatureName][i] = creatTree(Newdict_PlusFeatures, Newdict_NegativeFeatures, Newsum_Features,
                                             NewpreData)

    return Tree


"""
dict_PlusFeatures:{'Outlook': {'overcast': 2, 'rainy': 3, 'sunny': 1}, 'temperature': {'hot': 1, 'mild': 2, 'cool': 3}, 'humidity': {'high': 2, 'normal': 4}, 'windy': {'FALSE': 5, 'TRUE': 1}, 'play': 6}
dict_NegativeFeatures:{'Outlook': {'sunny': 3, 'rainy': 1, 'overcast': 0}, 'temperature': {'hot': 2, 'cool': 1, 'mild': 1}, 'humidity': {'high': 3, 'normal': 1}, 'windy': {'FALSE': 2, 'TRUE': 2}, 'play': 4}
sum_Features:{'Outlook': {'sunny': 4, 'overcast': 2, 'rainy': 4}, 'temperature': {'hot': 3, 'mild': 3, 'cool': 4}, 'humidity': {'high': 5, 'normal': 5}, 'windy': {'FALSE': 7, 'TRUE': 3}, 'play': 10}
{'Outlook': {'rainy': {'windy': {'TRUE': 'no', 'FALSE': 'yes'}}, 'sunny': {'temperature': {'cool': 'yes', 'hot': 'no', 'mild': 'no'}}, 'overcast': 'yes'}}
"""


def test(preData, Tree, nub, book):
    for i in Tree.keys():
        temp = i
    tempkey = preData[nub][book[temp]]
    if Tree[temp][tempkey] == 'yes':
        print('predict is yes')
        return
    elif Tree[temp][tempkey] == 'no':
        print('predict is no')
        return
    newTree = Tree[temp][tempkey]
    test(preData, newTree, nub, book)


if __name__ == '__main__':
    dict_PlusFeatures, dict_NegativeFeatures, sum_Features, preData = readFile()
    # print(preData)
    Tree = creatTree(dict_PlusFeatures, dict_NegativeFeatures, sum_Features, preData)
    print(json.dumps(Tree, indent=5))
    book = dict()
    for i in range(len(preData[0])):
        if preData[0][i] != 'play':
            book[preData[0][i]] = i
    test(preData, Tree, 5, book)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/529856.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

愿力,心力,能力

愿力&#xff0c;心力&#xff0c;能力 三力合一成点事 趣讲大白话&#xff1a;人与人的力量差别大 【趣讲信息科技165期】 *************************** 愿力是人文东方智慧 西方大概是mission使命之类 比如佛家发愿 儒家大概类似于立志 心力也是人文东方智慧 西方大概是意志…

51单片机(十七)红外遥控(外部中断)

❤️ 专栏简介&#xff1a;本专栏记录了从零学习单片机的过程&#xff0c;其中包括51单片机和STM32单片机两部分&#xff1b;建议先学习51单片机&#xff0c;其是STM32等高级单片机的基础&#xff1b;这样再学习STM32时才能融会贯通。 ☀️ 专栏适用人群 &#xff1a;适用于想要…

基于ssm的汽车服务平台

基于ssm的汽车售后服务平台 快速链接 基于ssm的汽车售后服务平台功能模块技术栈硬件环境功能截图 功能模块 用户 注册功能&#xff1a;用户通过注册功能进行访问平台预约服务功能&#xff1a;用户可以预约服务预约记录查询&#xff1a;用户可以查询自己预约记录也可以进行修改…

AIGPT中文版(人人都能使用的GPT工具)生活工作的好帮手。

AIGPT简介 AIGPT是一款非常强大的人工智能技术的语言处理工具软件&#xff0c;它具有 AI绘画 功能、AI写作、写论文、写代码、哲学探讨、创作等功能&#xff0c;可以说是生活和工作中的好帮手。 我们都知道使用ChatGPT是需要账号以及使用魔法的&#xff0c;其中的每一项对我们…

【框架源码】Spring底层IOC容器加入对象的方式

1.Spring容器加入对象方式简介 使用XML配置文件 在XML配置文件中使用< bean >标签来定义Bean&#xff0c;通过ClassPathXmlApplicationContext等容器来加载并初始化Bean。 使用注解 使用Spring提供的注解&#xff0c;例如Component、Service、Controller、Repository等注…

学习Python的day.14

模块学习 什么是模块&#xff1a; 打开Python解释器&#xff0c;定义了data 1&#xff0c; 然后去访问data是可以访问到的&#xff1b;关闭Python解释器&#xff0c;再打开&#xff0c;再去访问data&#xff0c;访问不到了。 假设我有1000行的代码&#xff0c;在python解释器…

pv操作练习题

信号量解决五个哲学家吃通心面问题 题型一 有五个哲学家围坐在一圆桌旁&#xff0c;桌中央有盘通心面&#xff0c;每人面前有一只空盘于&#xff0c;每两人之间放一把叉子。每个哲学家思考、饥饿、然后吃通心面。为了吃面&#xff0c;每个哲学家必须获得两把叉子&#xff0c;…

【机器视觉1】坐标系定义

坐标系定义 1. 图像坐标系2. 摄像机坐标系3. 世界坐标系4. 三种坐标系间的转换4.1 摄像机坐标系与无畸变图像坐标系之间的变换4.2 世界坐标系与摄像机坐标系之间的变换4.3 世界坐标系与无畸变图像坐标系之间的变换 1. 图像坐标系 数字图像坐标系&#xff1a; O 0 − u v O_0-u…

【差分+操作】C. Helping the Nature

Problem - 1700C - Codeforces 题意&#xff1a; 思路&#xff1a; 一开始手玩了一下 如果不是高低高的形式&#xff0c;那么一定不能通过操作3把全部元素变成0 因此就是先把所有元素变成高低高的形式 但是低在什么地方不确定 因此考虑枚举中间低谷位置&#xff0c;O(1)计…

【多微电网】基于粒子群优化算法的面向配电网的多微电网协调运行与优化(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

软件测试岗,4轮成功拿下字节 Offer,面试题复盘(附答案)

一共经历了四轮面试&#xff1a;技术4面&#xff0b;HR面。 特整理出所涉及的全部知识点&#xff0c;并复盘了完整面试题及答案&#xff0c;分享给大家&#xff0c;希望能够帮到一些计划面试字节的朋友。 一、测试基础理论类 怎么编写案例?软件测试的两种方法测试结束的标准…

allegro查看板子元器件的pin脚总数

怎么在ALLEGRO里统计焊盘和包括芯片pin和阻容的pad&#xff1f; 板子要拿出去布&#xff0c;需要根据焊盘计费&#xff1f; 方法一&#xff1a; 在find里面只勾选pin&#xff0c;然后鼠标左键&#xff0c;选择全部的pin 再选择菜单Display–element&#xff0c;如下图&#x…

Vivado综合属性系列之三 RAM_STYLE

目录 一、前言 二、RAM_STYLE ​ ​2.1 工程代码 ​ ​2.2 参考资料 一、前言 ​ ​RAM英文全称为Random Access Memory&#xff0c;随机存取存储器&#xff0c;可以实现数据的快速随机读写&#xff0c;RAM可直接verilog代码编写&#xff0c;也可调用IP核。 二、RAM…

Nginx 安装配置

Nginx("engine x")是一款是由俄罗斯的程序设计师Igor Sysoev所开发高性能的 Web和 反向代理 服务器&#xff0c;也是一个 IMAP/POP3/SMTP 代理服务器。在高连接并发的情况下&#xff0c;Nginx是Apache服务器不错的替代品。 Nginx 安装 系统平台&#xff1a;CentOS …

Java-Thread知识点汇总

什么是线程 记得小时候的电脑老是很卡,打开一个浏览器、游戏啥的老是卡死&#xff0c;关又关不掉&#xff0c;然后就会打开任务管理器&#xff0c;强制关闭它 我们可以看到&#xff0c;这个叫“进程”&#xff0c;简单理解一下&#xff0c;进程就是由&#xff08;一个或多个&am…

入河排污口设置论证报告书如何编制?入河排污口水质影响预测方法有哪些

随着水资源开发利用量不断增大&#xff0c;全国废污水排放量与日俱增&#xff0c;部分河段已远远超出水域纳污能力。近年来,部分沿岸入河排污口设置不合理&#xff0c;超标排污、未经同意私设排污口等问题逐步显现&#xff0c;已威胁到供水安全、水环境安全和水生态安全&#x…

Bootstrap开发之——Bootstrap简介(01)

一 概述 Bootstrap概念学习前需要具备知识查阅Bootstrap文档Bootstrap各版本有什么不同 二 Bootstrap概念 Bootstrap是一个使用HTML、CSS和JavaScript框架的前端开发框架Bootstrap 是全球最受欢迎的前端框架&#xff0c;用于构建响应式、移动设备优先的网站简洁、直观、强悍的…

Linux-基础指令-3

时间相关的指令 date显示 date 指定格式显示时间&#xff1a; date %Y:%m:%d 例子&#xff1a; 而上述中的 %Y %m %d 等等这些中间可以用很多的符号来分割&#xff0c; 如&#xff1a;" - " " _ " " : " 等等这些都是可以的&#xff0c;但是…

5月份了,不会还有人没找到工作吧.....

前两天跟朋友感慨&#xff0c;去年的铜九铁十、裁员、疫情导致好多人都没拿到offer&#xff01;现在都已经5月了&#xff0c;金三银四都结束一段时间了。 金三银四都已经结束&#xff0c;大部分企业也招到了自己需要的人&#xff0c;但是我看我的读者们还是有很大一部分人在抱…

算法套路十六——DP求解最长递增子序列LIS

算法示例&#xff1a;LeetCode300. 最长递增子序列 给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列&#xff0c;删除&#xff08;或不删除&#xff09;数组中的元素而不改变其余元素的顺序。例如&#xff0c;[3,6,2,7] …