决策树原理与python代码实现

研究背景

在很多实际的分类问题中,描述对象的特征都不是直接的数值特征,比如物体的颜色、形状,人的性别、民族、职业等等,这些特征被称为名义特征,它们通常只能比较相同或不相同,无法比较相似性,无法比较大小;另外有一类特征,它们本身是一种数值,比如序号、分级等,它们可能有顺序,但是却不能看作是欧式空间中的数值,这些特征叫做序数特征;还一些特征,它们本身为数值特征,比如年龄、成绩、温度等,在某些问题中,它们与研究目标之间的关系呈现出明显的非线性,比如某些疾病在不同的年龄段有很大差异,温度在不同区段对研究对象影响不同等,这些特征也不能作为普通的数值特征,需要分区段处理,最常见的是区间数据,它们的取值是实数,可以比较大小,但没有一个“自然地”零,比值没有意义(比如不能说“今天比昨天热一倍”等)。
对于这些数值特征,我们可以将它们数值化,但引入这一额外步骤可能会损失数据中的信息,也可能引入人为的信息,因此我们最好能有一种直接利用非数值特征进行分类的方法。决策树就是其中一种。

决策树问题实例

人们日常进行的树状决策过程,基本上根据相关专业知识或者多年积累的经验进行的。而所谓的决策树方法则是利用一定的训练样本,从数据中“学习”出决策规则,自动构造出决策树。下面用一个汽车推销员对潜在客户的分析为例说明决策树方法的基本原理。
假定某推销员根据调查得知,顾客是否会购买汽车,与顾客的年龄、性别和家庭收入关系最大,于是收集数据得到了如下的客户信息表格1。
顾客编号 年龄 性别 月收入 是否购买 1 21 男 4000 否 2 33 女 5000 否 3 30 女 3800 否 4 38 女 2000 否 5 25 男 7000 否 6 32 女 2500 否 7 20 女 2000 否 8 26 女 9000 是 9 32 男 5000 是 10 24 男 7000 否 11 40 女 4800 否 12 28 男 2800 否 13 35 女 4500 否 14 33 男 2800 是 15 37 男 4000 是 16 31 女 2500 否 \begin{array}{c|c|c|c|c} \text{顾客编号}&\text{年龄}&\text{性别}&\text{月收入}&\text{是否购买}\\ \hline 1&21&\text{男}&4000&\text{否}\\ \hline 2&33&\text{女}&5000&\text{否}\\ \hline 3&30&\text{女}&3800&\text{否}\\ \hline 4&38&\text{女}&2000&\text{否}\\ \hline 5&25&\text{男}&7000&\text{否}\\ \hline 6&32&\text{女}&2500&\text{否}\\ \hline 7&20&\text{女}&2000&\text{否}\\ \hline 8&26&\text{女}&9000&\text{是}\\ \hline 9&32&\text{男}&5000&\text{是}\\ \hline 10&24&\text{男}&7000&\text{否}\\ \hline 11&40&\text{女}&4800&\text{否}\\ \hline 12&28&\text{男}&2800&\text{否}\\ \hline 13&35&\text{女}&4500&\text{否}\\ \hline 14&33&\text{男}&2800&\text{是}\\ \hline 15&37&\text{男}&4000&\text{是}\\ \hline 16&31&\text{女}&2500&\text{否}\\ \end{array} 顾客编号12345678910111213141516年龄21333038253220263224402835333731性别月收入4000500038002000700025002000900050007000480028004500280040002500是否购买
面对这些数据我们无法进行分析,可以对年龄与收入进行分级。对于年龄,以30岁为分界线分为两档;对于月收入,以3000元、6000元为界限分为低中高三档。于是表1的数据可以整理为如下表2的形式。
顾客编号 年龄 性别 月收入 是否购买 1 < 30 男 中 否 2 ≥ 30 女 中 否 3 ≥ 30 女 中 否 4 ≥ 30 女 低 否 5 < 30 男 高 否 6 ≥ 30 女 低 否 7 < 30 女 低 否 8 < 30 女 高 是 9 ≥ 30 男 中 是 10 < 30 男 高 否 11 ≥ 30 女 中 否 12 < 30 男 低 否 13 ≥ 30 女 中 否 14 ≥ 30 男 低 是 15 ≥ 30 男 中 是 16 ≥ 30 女 低 否 \begin{array}{c|c|c|c|c} \text{顾客编号}&\text{年龄}&\text{性别}&\text{月收入}&\text{是否购买}\\ \hline 1&<30&\text{男}&\text{中}&\text{否}\\ \hline 2&\geq30&\text{女}&\text{中}&\text{否}\\ \hline 3&\geq30&\text{女}&\text{中}&\text{否}\\ \hline 4&\geq30&\text{女}&\text{低}&\text{否}\\ \hline 5&<30&\text{男}&\text{高}&\text{否}\\ \hline 6&\geq30&\text{女}&\text{低}&\text{否}\\ \hline 7&<30&\text{女}&\text{低}&\text{否}\\ \hline 8&<30&\text{女}&\text{高}&\text{是}\\ \hline 9&\geq30&\text{男}&\text{中}&\text{是}\\ \hline 10&<30&\text{男}&\text{高}&\text{否}\\ \hline 11&\geq30&\text{女}&\text{中}&\text{否}\\ \hline 12&<30&\text{男}&\text{低}&\text{否}\\ \hline 13&\geq30&\text{女}&\text{中}&\text{否}\\ \hline 14&\geq30&\text{男}&\text{低}&\text{是}\\ \hline 15&\geq30&\text{男}&\text{中}&\text{是}\\ \hline 16&\geq30&\text{女}&\text{低}&\text{否}\\ \end{array} 顾客编号12345678910111213141516年龄<30303030<3030<30<3030<3030<3030303030性别月收入是否购买
决策树是由一系列节点组成的,每一个节点代表一个特征和相应的决策规则。最上部的节点是根节点(这里的“树”通常是倒置过来画的,即根在顶端),此时所有的样本都在一起,经过该节点后被划分到各个子节点中。每个子节点再用新的特征来进一步决策,直到最后的叶节点。在叶节点上,每一个节点只包含单纯一类的样本,不需要再划分。
决策树的构建过程就是选取特征和确定决策规则的过程。在这个例子里,有年龄、性别、月收入三个特征,首先需要确定第一步用哪个特征构造根节点。

ID3方法

ID3方法是一种构建决策树的方法,其基础是香农信息论中定义的熵。在信息论的定义中,假设一个事件有 k k k种可能的结果,每种对应的概率为 P i , i = 1 , ⋯   , k P_i,i=1,\cdots,k Pi,i=1,,k,则此事件的信息量可以用如下定义的来度量 I = − ( P 1 l o g 2 P 1 + P 2 l o g 2 P 2 + ⋯ + P k l o g 2 P k = − ∑ i = 1 k P i l o g 2 P i I=-(P_1{\rm log}_2P_1+P_2{\rm log}_2P_2+\cdots+P_k{\rm log}_2P_k=-\sum_{i=1}^{k}P_i{\rm log}_2P_i I=(P1log2P1+P2log2P2++Pklog2Pk=i=1kPilog2Pi容易知道,样本类别越少,对应的熵不纯度也越小,当样本全部为同一类别时,熵不纯度为0
对于表2中的数据,在不考虑任何特征时,16人中有4人买车,12人不买车,此时的熵不纯度为 I ( 16 , 4 ) = − ( 4 16 l o g 2 4 16 + 12 16 l o g 2 12 16 ) = 0.8113 I(16,4)=-\left(\frac{4}{16}{\rm log_2}\frac{4}{16}+\frac{12}{16}{\rm log_2}\frac{12}{16}\right)=0.8113 I(16,4)=(164log2164+1612log21612)=0.8113
其中 I ( 16 , 4 ) I(16,4) I(16,4)表示总共16个样本中4个为一类,12个作为另一类时的熵不纯度。现在我们需要找到一个能够最大程度划分是否买车的特征。当引入某一特征进行一次划分后,熵不纯度会降低,而使该值降低最多的特征就是我们要找的。
若以年龄为根节点,则所有样本分为两组:30岁以下的组中,共有6人,有1人买车;30岁及以上的组中,共有10人,3人买车。总的不纯度计算如下: I a g e = 6 16 I ( 6 , 1 ) + 10 16 I ( 10 , 3 ) = 0.7946 I_{age}=\frac{6}{16}I(6,1)+\frac{10}{16}I(10,3)=0.7946 Iage=166I(6,1)+1610I(10,3)=0.7946
这样,熵不纯度的减少量为 Δ I a g e = 0.8113 − 0.7946 = 0.0167 \Delta I_{age}=0.8113-0.7946=0.0167 ΔIage=0.81130.7946=0.0167
类似的,计算出以性别和收入为根节点的减少量为 Δ I g e n d e r = 0.0972 \Delta I_{gender}=0.0972 ΔIgender=0.0972 Δ I i n c o m e = 0.0177 \Delta I_{income}=0.0177 ΔIincome=0.0177
由此可见以性别作为根节点是最合适的,于是我们可以把决策树的第一层绘制出来,如下:
在这里插入图片描述
接下来,对于女性组与男性组再进行上面同样的过程,可以得到女性组以月收入为下一节点,而男性组以年龄作为下一节点。此时最后一级的样本已经是纯党的样本,因此决策树也就构建完成了,如下所示:
在这里插入图片描述
由此就得到了该简单问题的解决办法,若此时有一名新顾客,就可以根据该决策树的判断规则对其是否可能会买车做出预测

ID3方法的python实现

下面我们通过python来实现ID3方法构建决策树

导入所需库

import math
from collections import Counter

我们需要用到math库中的 l o g 2 \rm log_2 log2函数,Counter可以帮助我们对列表中的字符串进行计数

创建决策树类DecisionTree

class DecisionTree:
    
    def __init__(self):
        self.tree = {}
    
    def calculate_entropy(self, data):#计算data数据集的熵不纯度
        labels = [d[-1] for d in data]
        label_counts = Counter(labels)
        entropy = 0.0
        for key in label_counts:
            prob = float(label_counts[key]) / len(data)
            entropy -= prob * math.log2(prob)
        return entropy
    
    def split_data(self, data, axis, value):#拆分数据集(将特征axis的值为value的样本提取出来)
        sub_data = []
        for d in data:
            if d[axis] == value:
                reduced_d = d[:axis]
                reduced_d.extend(d[axis+1:])
                sub_data.append(reduced_d)
        return sub_data
    
    def choose_best_feature(self, data):#寻找使熵不纯度降低最大的分类节点
        num_features = len(data[0]) - 1
        base_entropy = self.calculate_entropy(data)
        best_info_gain = 0.0
        best_feature = -1
        for i in range(num_features):
            feat_list = [d[i] for d in data]
            unique_vals = set(feat_list)
            new_entropy = 0.0
            for value in unique_vals:
                sub_data = self.split_data(data, i, value)
                prob = len(sub_data) / len(data)
                new_entropy += prob * self.calculate_entropy(sub_data)
            info_gain = base_entropy - new_entropy
            if info_gain > best_info_gain:
                best_info_gain = info_gain
                best_feature = i
        return best_feature
    
    def majority_label(self, labels):#确定节点名称
        label_counts = Counter(labels)
        majority_label = label_counts.most_common(1)[0][0]
        return majority_label
    
    def create_tree(self, data, features):#创建决策树
        labels = [d[-1] for d in data]
        if labels.count(labels[0]) == len(labels):
            return labels[0]
        if len(data[0]) == 1:
            return self.majority_label(labels)
        best_feature = self.choose_best_feature(data)
        best_feature_label = features[best_feature]
        tree = {best_feature_label: {}}
        del(features[best_feature])
        feat_values = [d[best_feature] for d in data]
        unique_vals = set(feat_values)
        for value in unique_vals:
            sub_features = features[:]
            tree[best_feature_label][value] = self.create_tree(self.split_data(data, best_feature, value), sub_features)
        return tree
    
    def fit(self, data, features):
        self.tree = self.create_tree(data, features)
    
    def classify(self, sample, tree):#分类
        if isinstance(tree, str):
            return tree
        feature = list(tree.keys())[0]
        value = sample[feature]
        sub_tree = tree[feature][value]
        return self.classify(sample, sub_tree)
    
    def predict(self, samples):#预测
        predictions = []
        for sample in samples:
            prediction = self.classify(sample, self.tree)
            predictions.append(prediction)
        return predictions

DecisionTree类实例化解决问题

if __name__ == '__main__':
    # 创建数据集
    data = [['<30', '男', '中', '否'],
            ['≥30', '女', '中', '否'],
            ['≥30', '女', '中', '否'],
            ['≥30', '女', '低', '否'],
            ['<30', '男', '高', '否'],
            ['≥30', '女', '低', '否'],
            ['<30', '女', '低', '否'],
            ['<30', '女', '高', '是'],
            ['≥30', '男', '中', '是'],
            ['<30', '男', '高', '否'],
            ['≥30', '女', '中', '否'],
            ['<30', '男', '低', '否'],
            ['≥30', '女', '中', '否'],
            ['≥30', '男', '低', '是'],
            ['≥30', '男', '中', '是'],
            ['≥30', '女', '低', '否' ]]
    
    features = ['年龄', '性别', '月收入']
    
    # 创建决策树实例
    dt = DecisionTree()
    
    # 构建决策树
    dt.fit(data, features)
    print(dt.tree)
    # 预测样本的分类
    samples = [
        {'年龄': '<30', '性别': '男', '月收入': '低'},
        {'年龄': '≥30', '性别': '女', '月收入': '中'}
    ]
    
    predictions = dt.predict(samples)
    print(predictions)

在上面的过程中,我们根据表2创建了数据集,然后定义了类的实例dt,dt根据数据集可以完成决策树的构建,并根据其对新样本进行预测
输出结果为:
在这里插入图片描述
可以看出其与我们上面得到的结果是相同的,即完成了ID3方法的代码实现

ID3方法的改进

ID3算法是一种经典的决策树算法,其核心思想是通过计算信息增益来选择最佳的特征进行划分。信息增益是根据特征将数据集分成不同子集后,获得的关于分类结果的信息量的增加量。ID3算法在特征选择时,倾向于选择具有更多取值的特征,即具有更多分支的特征。这可能导致过度拟合和对噪声数据过于敏感。
为了解决ID3算法的一些问题,ID5R算法进行了改进和扩展。ID5R算法使用了C4.5算法,并采用了信息增益比来选择最佳特征进行划分。信息增益比是信息增益与划分信息的比值,可以避免偏向具有较多取值的特征。此外,ID5R算法还引入了对缺失值处理、连续特征处理和剪枝的策略,使得决策树更加准确和健壮。
总结而言,ID5R算法是在ID3算法基础上进行了改进和扩展,旨在提高决策树的性能和普适性。

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值