广州大学机器学习与数据挖掘实验二:决策树算法

相关资料

广州大学机器学习与数据挖掘实验一:线性回归
广州大学机器学习与数据挖掘实验二:决策树算法
广州大学机器学习与数据挖掘实验三:图像分类
广州大学机器学习与数据挖掘实验四:Apriori算法
四份实验报告下载链接🔗

一、实验目的

本实验课程是计算机、智能、物联网等专业学生的一门专业课程,通过实验,帮助学生更好地掌握数据挖掘相关概念、技术、原理、应用等;通过实验提高学生编写实验报告、总结实验结果的能力;使学生对机器学习算法、数据挖掘实现等有比较深入的认识。
1.掌握机器学习中涉及的相关概念、算法。
2.熟悉数据挖掘中的具体编程方法;
3.掌握问题表示、求解及编程实现。

二、基本要求

1.实验前,复习《机器学习与数据挖掘》课程中的有关内容。
2.准备好实验数据。
3.编程要独立完成,程序应加适当的注释。
4.完成实验报告。

三、实验软件

使用Python或R语言实现。

四、实验内容

表4.1的西瓜数据集包括17个训练样例,请学习一个决策树,用于预测一个西瓜是不是好瓜。具体要求如下:
(1)在生成决策树的过程中,请分别使用ID3算法、C4.5算法、CART算法,并对比三个算法结果。其中,,ID3算法在生成决策树的各结点使用信息增益准则进行特征选择;C4.5算法在生成决策树的过程使用信息增益比进行特征选择;CART算法假设决策树是一个二叉树通过递归地二分每个特征,将特征空间划分为有限个单元,并在这些单元上确定预测的概率分布。
(2)请自己写程序实现决策树的生成过程,不要直接调用决策树算法包。
在这里插入图片描述

五、实验过程

三个算法均以数据结构——树相关,因此可以采用递归算法进行生成决策树,对于输入数据分别按照信息增益、信息增益比、基尼指数去进行多分类或二分类,然后递归调用划分子树算法,最终生成一颗决策树。
对于ID3算法,我们选择信息增益去选择划分特征;
对于C4.5算法,我们选择信息增益比去选择划分特征;
对于CART算法,我们选择基尼指数去选择细粒度更低的特征内的属性去进行二分类划分,比如纹理是否清晰等等。

六、实验结果与评估

ID3算法可视化如下:
在这里插入图片描述

C4.5算法可视化如下:

在这里插入图片描述

CART算法可视化如下:

在这里插入图片描述

决策树算法特征选择是否二叉树非叶子节点叶子节点
ID3信息增益58
C4.5信息增益比57
CART基尼指数67

评价:
1、三种算法由于特征选择的度量不同,可视化出来的决策树也不一样,而且决策树深度由于样本数量不多保持一致,均都是5。
2、可以看到CART由于使用的是二叉树,非叶子节点会多于其他两个算法。
3、非叶子节点的个数可以反映该决策树是否容易过拟合,非叶子节点个数越多的决策树越容易过拟合,这里也可以看出CART和C4.5要略优于ID3算法,但样本数量不多,不能准确下定论。
4、额外的拓展,CART全称分类和回归树,二叉树的特性可以让它不仅适用于分类问题,也可用于回归问题。

七、实验代码

from math import log2
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties



# 计算标签出现次数
def get_counts(data):
    total = len(data)
    results = {}
    for d in data:
        results[d[-1]] = results.get(d[-1], 0) + 1
    return results, total


# 计算信息熵
def calcu_entropy(data):
    results, total = get_counts(data)
    ent = sum([-1.0 * v / total * log2(v / total) for v in results.values()])
    return ent # 熵


# ID3算法,计算每个feature的信息增益
def calcu_each_gain(column, update_data):
    total = len(column)
    grouped = update_data.iloc[:, -1].groupby(by=column)

    temp = sum([len(g[1]) / total * calcu_entropy(g[1]) for g in list(grouped)])
    return calcu_entropy(update_data.iloc[:, -1]) - temp #熵-条件熵


# C4.5算法,计算每个feature的信息增益率
def calcu_each_gain_rate(column, update_data):
    total = len(column)
    grouped = update_data.iloc[:, -1].groupby(by=column) #分组
    temp = sum([len(g[1]) / total * calcu_entropy(g[1]) for g in list(grouped)])

    H_a = -1*sum(len(g[1])/total *  log2(len(g[1])/total) for g in list(grouped)) #惩罚参数
    gain_rate = (calcu_entropy(update_data.iloc[:, -1]) -temp) / H_a
    return gain_rate  #(熵-条件熵)/惩罚参数


# CART算法,计算每个feature内每个取值的基尼系数
def calcu_each_Gini_value(name, update_data):
    column_count = dict([(ds, list(pd.unique(update_data[ds]))) for ds in update_data.iloc[:, :-1].columns])
    column_name = ''
    for key, value in zip(column_count.keys(), column_count.values()):
        if name in value:
            column_name = key
            break

    grouped = update_data.iloc[:, -1].groupby(by=update_data[column_name]) #分组

    first_group = [g for g in list(grouped) if g[0]==name]
    first_group = list(first_group[0][1])
    second_group = []
    [second_group.extend(g[1]) for g in list(grouped) if g[0] != name]

    a  = first_group.count('是')
    len1 = len(first_group)
    b = second_group.count('是')
    len2 = len(second_group)
    lens = len1+len2

    if(len2 == 0):return  len1 /lens *(2* a/len1*(1-a/len1))
    if (len1 == 0): return len2 / lens*(2*b/len2*(1-b/len2))
    return len1 /lens *(2* a/len1*(1-a/len1)) + len2 / lens*(2*b/len2*(1-b/len2))


# 获取最大的信息增益、信息增益率或最小的基尼系数的feature
def get_max_gain(temp_data,al_name):
    if al_name == 'ID3':
        columns_entropy = [(col, calcu_each_gain(temp_data[col], temp_data)) for col in temp_data.iloc[:, :-1]] #ID3算法:信息增益
        columns_entropy = sorted(columns_entropy, key=lambda f: f[1], reverse=True)

    elif al_name == 'C4.5':
        columns_entropy = [(col, calcu_each_gain_rate(temp_data[col], temp_data)) for col in temp_data.iloc[:, :-1]] #C4.5算法:信息增益率
        columns_entropy = sorted(columns_entropy, key=lambda f: f[1], reverse=True)

    elif al_name == 'CART':
        att_cat = []
        [att_cat.extend(list(pd.unique(temp_data[ds]))) for ds in temp_data.iloc[:, :-1].columns]
        columns_entropy = [(col, calcu_each_Gini_value(col, temp_data)) for col in att_cat]
        columns_entropy = sorted(columns_entropy, key=lambda f: f[1], reverse=False)

    print(columns_entropy)
    return columns_entropy[0]



# 去掉数据中已存在的列属性内容,在CART树中需要注意
def drop_exist_feature(data, best_feature, al_name,inner_feature=""):
    if al_name =='CART':
        new_data = data[data[best_feature] == inner_feature]
        inner_data = data[data[best_feature] != inner_feature]

        new_data = new_data.drop(best_feature, axis=1)

        new_data = [(inner_feature , new_data ),("非"+inner_feature ,inner_data )]

    else:
        attr = pd.unique(data[best_feature])
        new_data = [(nd, data[data[best_feature] == nd]) for nd in attr]
        new_data = [(n[0], n[1].drop([best_feature], axis=1)) for n in new_data]


    return new_data


# 获得出现最多的label
def get_most_label(label_list):
    label_dict = {}
    for l in label_list:
        label_dict[l] = label_dict.get(l, 0) + 1
    sorted_label = sorted(label_dict.items(), key=lambda ll: ll[1], reverse=True)
    return sorted_label[0][0]


# 创建决策树
def create_tree(data_set, column_count,al_name):
    label_list = data_set.iloc[:, -1]  #获得标签
    if len(pd.unique(label_list)) == 1:  #终止条件,全部为一类,返回标签
        return label_list.values[0]
    if all([len(pd.unique(data_set[i])) == 1 for i in data_set.iloc[:, :-1].columns]): #终止条件,类别属性全部为一类,返回标签同属一类最多的标签
        return get_most_label(label_list)
    best_attr = get_max_gain(data_set,al_name)[0] #获得最大收益对应的属性名

    if al_name == 'CART':
        column_name = ''
        for key, value in zip(column_count.keys(), column_count.values()):
            if best_attr in value:
                column_name = key
                break

        tree = {'是否' + best_attr: {}}
        for item in drop_exist_feature(data_set, column_name,al_name,best_attr):
            tree['是否' + best_attr][item[0]+':'+str(item[1].shape[0])] = create_tree(item[1], column_count,al_name)


    else:
        tree = {best_attr: {}}

        for item in drop_exist_feature(data_set, best_attr,al_name):
            tree[best_attr][item[0]+':'+str(item[1].shape[0])] = create_tree(item[1], column_count,al_name)
    return tree


# 获取树的叶子节点数目
def get_num_leafs(decision_tree):
    num_leafs = 0
    first_str = next(iter(decision_tree))
    second_dict = decision_tree[first_str]
    for k in second_dict.keys():
        if isinstance(second_dict[k], dict):
            num_leafs += get_num_leafs(second_dict[k])
        else:
            num_leafs += 1
    return num_leafs


# 获取树的深度
def get_tree_depth(decision_tree):
    max_depth = 0
    first_str = next(iter(decision_tree))
    second_dict = decision_tree[first_str]
    for k in second_dict.keys():
        if isinstance(second_dict[k], dict):
            this_depth = 1 + get_tree_depth(second_dict[k])
        else:
            this_depth = 1
        if this_depth > max_depth:
            max_depth = this_depth
    return max_depth


# 绘制节点
def plot_node(node_txt, center_pt, parent_pt, node_type):
    arrow_args = dict(arrowstyle='<-')
    font = FontProperties(fname=r"/System/Library/Fonts/STHeiti Medium.ttc", size=15)
    create_plot.ax1.annotate(node_txt, xy=parent_pt, xycoords='axes fraction', xytext=center_pt,
                             textcoords='axes fraction', va="center", ha="center", bbox=node_type,
                             arrowprops=arrow_args, FontProperties=font)


# 标注划分属性
def plot_mid_text(cntr_pt, parent_pt, txt_str):
    font = FontProperties(fname=r"/System/Library/Fonts/STHeiti Medium.ttc", size=10)
    x_mid = (parent_pt[0] - cntr_pt[0]) / 2.0 + cntr_pt[0]
    y_mid = (parent_pt[1] - cntr_pt[1]) / 2.0 + cntr_pt[1]
    create_plot.ax1.text(x_mid, y_mid, txt_str, va="center", ha="center", color='red', FontProperties=font)


# 绘制决策树,参考书籍《机器学习实战》绘图代码
def plot_tree(decision_tree, parent_pt, node_txt):
    d_node = dict(boxstyle="sawtooth", fc="0.8")
    leaf_node = dict(boxstyle="round4", fc='0.8')
    num_leafs = get_num_leafs(decision_tree)
    first_str = next(iter(decision_tree))
    cntr_pt = (plot_tree.xoff + (1.0 + float(num_leafs)) / 2.0 / plot_tree.totalW, plot_tree.yoff)
    plot_mid_text(cntr_pt, parent_pt, node_txt)
    plot_node(first_str, cntr_pt, parent_pt, d_node)
    second_dict = decision_tree[first_str]
    plot_tree.yoff = plot_tree.yoff - 1.0 / plot_tree.totalD
    for k in second_dict.keys():
        if isinstance(second_dict[k], dict):
            plot_tree(second_dict[k], cntr_pt, k)
        else:
            plot_tree.xoff = plot_tree.xoff + 1.0 / plot_tree.totalW
            plot_node(second_dict[k], (plot_tree.xoff, plot_tree.yoff), cntr_pt, leaf_node)
            plot_mid_text((plot_tree.xoff, plot_tree.yoff), cntr_pt, k)
    plot_tree.yoff = plot_tree.yoff + 1.0 / plot_tree.totalD


def create_plot(dtree):
    fig = plt.figure(1, facecolor='white')
    fig.clf()
    axprops = dict(xticks=[], yticks=[])
    create_plot.ax1 = plt.subplot(111, frameon=False, **axprops)
    plot_tree.totalW = float(get_num_leafs(dtree))
    plot_tree.totalD = float(get_tree_depth(dtree))
    plot_tree.xoff = -0.5 / plot_tree.totalW
    plot_tree.yoff = 1.0
    plot_tree(dtree, (0.5, 1.0), '')
    plt.show()


if __name__ == '__main__':
    watermelon_data = pd.read_csv('./watermelon.csv')
    column_count = dict([(ds, list(pd.unique(watermelon_data[ds]))) for ds in watermelon_data.iloc[:, :-1].columns])


    al_name = 'ID3' 
    d_tree = create_tree(watermelon_data, column_count,al_name)
    print('ID3算法:',d_tree)
    create_plot(d_tree)

    al_name =  'C4.5'
    d_tree = create_tree(watermelon_data, column_count, al_name)
    print('C4.5算法:',d_tree)
    create_plot(d_tree)

    al_name = 'CART'
    d_tree = create_tree(watermelon_data, column_count, al_name)
    print('CART算法:',d_tree)
    create_plot(d_tree)
  • 4
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wujiekd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值