Taking judging whether a watermelon is good or bad as an example, the generated decision tree (source code) -- previous article -- the following of (decision tree)


from random import choice
from collections import Counter
import math

# ==========
# 定义数据集
# ==========
D = [
    {
    
    '色泽': '青绿', '根蒂': '蜷缩', '敲声': '浊响', '纹理': '清晰', '脐部': '凹陷', '触感': '硬滑', '好瓜': '是'},
    {
    
    '色泽': '乌黑', '根蒂': '蜷缩', '敲声': '沉闷', '纹理': '清晰', '脐部': '凹陷', '触感': '硬滑', '好瓜': '是'},
    {
    
    '色泽': '乌黑', '根蒂': '蜷缩', '敲声': '浊响', '纹理': '清晰', '脐部': '凹陷', '触感': '硬滑', '好瓜': '是'},
    {
    
    '色泽': '青绿', '根蒂': '蜷缩', '敲声': '沉闷', '纹理': '清晰', '脐部': '凹陷', '触感': '硬滑', '好瓜': '是'},
    {
    
    '色泽': '浅白', '根蒂': '蜷缩', '敲声': '浊响', '纹理': '清晰', '脐部': '凹陷', '触感': '硬滑', '好瓜': '是'},
    {
    
    '色泽': '青绿', '根蒂': '稍蜷', '敲声': '浊响', '纹理': '清晰', '脐部': '稍凹', '触感': '软粘', '好瓜': '是'},
    {
    
    '色泽': '乌黑', '根蒂': '稍蜷', '敲声': '浊响', '纹理': '稍糊', '脐部': '稍凹', '触感': '软粘', '好瓜': '是'},
    {
    
    '色泽': '乌黑', '根蒂': '稍蜷', '敲声': '浊响', '纹理': '清晰', '脐部': '稍凹', '触感': '硬滑', '好瓜': '是'},
    {
    
    '色泽': '乌黑', '根蒂': '稍蜷', '敲声': '沉闷', '纹理': '稍糊', '脐部': '稍凹', '触感': '硬滑', '好瓜': '否'},
    {
    
    '色泽': '青绿', '根蒂': '硬挺', '敲声': '清脆', '纹理': '清晰', '脐部': '平坦', '触感': '软粘', '好瓜': '否'},
    {
    
    '色泽': '浅白', '根蒂': '硬挺', '敲声': '清脆', '纹理': '模糊', '脐部': '平坦', '触感': '硬滑', '好瓜': '否'},
    {
    
    '色泽': '浅白', '根蒂': '蜷缩', '敲声': '浊响', '纹理': '模糊', '脐部': '平坦', '触感': '软粘', '好瓜': '否'},
    {
    
    '色泽': '青绿', '根蒂': '稍蜷', '敲声': '浊响', '纹理': '稍糊', '脐部': '凹陷', '触感': '硬滑', '好瓜': '否'},
    {
    
    '色泽': '浅白', '根蒂': '稍蜷', '敲声': '沉闷', '纹理': '稍糊', '脐部': '凹陷', '触感': '硬滑', '好瓜': '否'},
    {
    
    '色泽': '乌黑', '根蒂': '稍蜷', '敲声': '浊响', '纹理': '清晰', '脐部': '稍凹', '触感': '软粘', '好瓜': '否'},
    {
    
    '色泽': '浅白', '根蒂': '蜷缩', '敲声': '浊响', '纹理': '模糊', '脐部': '平坦', '触感': '硬滑', '好瓜': '否'},
    {
    
    '色泽': '青绿', '根蒂': '蜷缩', '敲声': '沉闷', '纹理': '稍糊', '脐部': '稍凹', '触感': '硬滑', '好瓜': '否'},
]


# ==========
# 决策树生成类
# ==========
class DecisionTree:
    def __init__(self, D, label, chooseA):
        self.D = D  # 数据集
        self.label = label  # 属性"好瓜" 作为标签 
        self.chooseA = chooseA  # 划分方法:根据信息增益
        self.A = list(filter(lambda key: key != label, D[0].keys()))  # 属性集合A(当然要去除"好瓜"这一判断属性了)

        # 获得A的每个className属性的可选项,加入到A_item集合中,如 色泽:对应 {'乌黑', '青绿', '浅白'}
        self.A_item = {
    
    }
        for a in self.A:
            self.A_item.update({
    
    a: set(self.getClassValues(D, a))})  # 此处的set,有个集合去重操作
            '''
            # print("self.A_item is"  ,self.A_item)
            最终的A_item:
            {
                '色泽': {'乌黑', '青绿', '浅白'}, 
                '根蒂': {'硬挺', '稍蜷', '蜷缩'}, 
                '敲声': {'清脆', '浊响', '沉闷'},
                '纹理': {'清晰', '稍糊', '模糊'}, 
                '脐部': {'稍凹', '凹陷', '平坦'}, 
                '触感': {'硬滑', '软粘'}
             }
            '''
        self.root = self.generate(self.D, self.A)  # 生成树并保存根节点
        # print("self.root is", self.root)

    # 获得D中所有className属性的值
    def getClassValues(self, D, className):
        '''
        :param D: 数据集
        :param className: 每个className
        :return: D中className属性对应的所有值
        '''
        return list(map(lambda sample: sample[className], D))

    # D中样本是否在A的每个属性上相同
    def isSameInA(self, D, A):
        for a in A:
            types = set(self.getClassValues(D, a))
            #print("types is ", types)
            if len(types) > 1:
                return False
        return True

    # 构建决策树,递归生成节点
    def generate(self, D, A):
        '''
        :param D: 数据集
        :param A: 所有className属性(不包含"好瓜")
        :return:
        '''
        node = {
    
    }  # 生成节点
        remainLabelValues = self.getClassValues(D, self.label)
        # D中的所有标签 如['是', '是', '是', '是', '是', '是', '是', '是', '否', '否', '否', '否', '否', '否', '否', '否', '否']

        remainLabelTypes = set(remainLabelValues)
        # D中含有哪几种标签 如{'是', '否'}

        if len(remainLabelTypes) == 1:  # 当前节点包含的样本全属于同个类别,无需划分
            return remainLabelTypes.pop()  # 标记Node为叶子结点,值为仅存的标签

        most = max(remainLabelTypes, key=remainLabelValues.count)  # D占比最多的标签 此处为"是"8个,"否"9个(此处为"否")

        if len(A) == 0 or self.isSameInA(D, A):
            # 当前属性集为空,或是所有样本在所有属性上取值相同,无法划分
            return most  # 标记Node为叶子结点,值为占比最多的标签

        # ******划分:******
        a = self.chooseA(D, A, self)  # a即为计算过的信息增益中(值最大)的className属性,作为节点,此时算出为"纹理"
        print("a(每次选的最大信息增益属性) is ", a)

        for type in self.A_item[a]:
            condition = (lambda sample: sample[a] == type)  # 决策条件
            remainD = list(filter(condition, D))  # 过滤掉不满足condition条件的样本
            #print("remainD is ",remainD)
            if len(remainD) == 0:
                # 当前节点包含的样本集为空,不能划分
                node.update({
    
    type: most})  # 标记Node为叶子结点,值为占比最多的标签
            else:
                # 继续对剩下的样本按其余属性划分
                remainA = list(filter(lambda x: x != a, A))  # 未使用的属性
                _node = self.generate(remainD, remainA)  # 递归生成子代节点
                node.update({
    
    type: _node})  # 把生成的子代节点更新到当前节点
        return {
    
    a: node}


# ==========
#  定义划分方法
# ==========

# 信息熵
def Ent(D, label, a, a_v):
    '''
    :param D:
    :param label:
    :param a: 某一具体className属性 如"色泽"
    :param a_v: 此属性对应的具体值 ,如"青绿、乌黑、浅白"三种中的一个
    :return:
    '''
    D_v = filter(lambda sample: sample[a] == a_v, D)  # 如此处,举个例子,选的是色泽="青绿"的瓜,共6个
    D_v = map(lambda sample: sample[label], D_v)  # 将色泽="青绿"的瓜按标签分类为好瓜=3个,坏瓜=3个
    D_v = list(D_v)
    D_v_length = len(D_v)
    counter = Counter(D_v)
    info_entropy = 0
    for k, v in counter.items():  # 计算色泽="青绿"信息熵
        p_k = v / D_v_length
        info_entropy += p_k * math.log(p_k, 2)
    return -info_entropy


# 信息增益
def information_gain(D, A, tree: DecisionTree):
    gain = {
    
    }
    for a in A:
        gain[a] = 0
        values = tree.getClassValues(D, a)
        counter = Counter(values)
        # print("counter.items() is", counter.items())
        for a_v, nums in counter.items():  # 分别计算所有className属性的信息增益,此处以属性=色泽为例注释
            gain[a] -= (nums / len(D)) * Ent(D, tree.label, a, a_v)  # 分别计算"青绿、乌黑、浅白"所对应的(信息熵 *该属性值所占比)
    #print("gain is ", gain)
    return max(gain.keys(), key=lambda key: gain[key])  # 选择信息增益最大的那个属性作为节点,此时算出来是"纹理"


# ==========
#  创建决策树
# ==========
if __name__ == '__main__':
    desicionTreeRoot = DecisionTree(D, label='好瓜', chooseA=information_gain).root
    print('决策树:', desicionTreeRoot)
最终生成的决策树如下:
a(每次选的最大信息增益属性) is  纹理
a(每次选的最大信息增益属性) is  触感
a(每次选的最大信息增益属性) is  根蒂
a(每次选的最大信息增益属性) is  色泽
a(每次选的最大信息增益属性) is  触感
决策树: {
    
    '纹理': {
    
    '稍糊': {
    
    '触感': {
    
    '软粘': '是', '硬滑': '否'}}, '清晰': {
    
    '根蒂': {
    
    '稍蜷': {
    
    '色泽': {
    
    '乌黑': {
    
    '触感': {
    
    '软粘': '否', '硬滑': '是'}}, '浅白': '是', '青绿': '是'}}, '蜷缩': '是', '硬挺': '否'}}, '模糊': '否'}}

If you have something to say, thank you for the materials provided by ww ~ Haha You said I am very happy

Guess you like

Origin blog.csdn.net/qq_45067943/article/details/121598172
Recommended