【数据挖掘】决策树分类C4.5算法(python实现)

一、python代码

'''
Author: Vici__
date: 2020/5/18
'''
import math 
import random
import numpy as np
from math import log
import operator

'''
1. 获取数据集
'''
def get_dataset():
    dataset = [['青年', '否', '否', '中', '否'],
                ['青年', '否', '否', '高', '否'],
                ['青年', '是', '否', '高', '是'],
                ['青年', '是', '是', '中', '是'],
                ['青年', '否', '否', '中', '否'],
                ['中年', '否', '否', '中', '否'],
                ['中年', '否', '否', '高', '否'],
                ['中年', '是', '是', '高', '是'],
                ['中年', '否', '是', '很高', '是'],
                ['中年', '否', '是', '很高', '是'],
                ['老年', '否', '是', '很高', '是'],
                ['老年', '否', '是', '高', '是'],
                ['老年', '是', '否', '高', '是'],
                ['老年', '是', '否', '很高', '是'],
                ['老年', '否', '否', '中', '否']]
    test_dataset = [['青年', '否', '否', '中'],
                    ['中年', '否', '否', '高'],
                    ['老年', '是', '否', '高']]
    labels = ['年龄', '婚否', '车否', '身高'] # 特征名称
    class_label = '贷款'
    return dataset, labels, class_label, test_dataset

'''
2. 计算信息熵
'''
def calc_shannonent(dataset):
    n = len(dataset) # 数据集长度
    shannonent = 0.0 # 初始化信息熵
    label_counts = {} # 标签与个数的映射, 形式:{'是':9, '否':6}
    # 遍历数据集,找到所有的标签,以及属于此标签的行数
    for data in dataset:
        label = data[-1] # 每行最后一列的值为标签
        if label not in label_counts: # 如果此标签未出现过
            label_counts[label] = 1 # 计数初始化为1
        else:
            label_counts[label] += 1 # 否则加1
    # 计算数据集D的信息熵,sum(-p*log2(p))
    for key in label_counts: 
        p = float(label_counts[key] / n) # 计算概率,属于此标签的数据占总数据的比例
        shannonent -= p * log(p, 2) # 计算信息熵
    return shannonent
'''
3. 分割数据集
'''
def split_dataset(dataset, axis, value): # axis是列号,value是值
    result = []
    for data in dataset:
        if data[axis] == value: # 如果该行数据axis列的值为value
            r = data[:axis] + data[axis+1:] # 就把这一行除axis列以外的元素放入r
            result.append(r)                # 意思就是,当前是以axis列的特征作为决策树分支标准的,
    return result                           # 那么再继续往下进行时,就要以除此特征之外的其它特征为标准分类

'''
4. 获取最佳特征,计算信息增益率,并比较
'''
def choose_best_feature(dataset):
    numFeatures = len(dataset[0]) - 1 # 特征个数,除去最后一列的分类标签
    ent = calc_shannonent(dataset) # 当前数据集的信息熵
    best_gain = 0.0 # 最佳信息熵
    best_feature_id = -1 # 最佳信息熵的特征编号
    infogain = 0.0 # 最佳信息增益率
    for i in range(numFeatures):
        unique_values = set([line[i] for line in dataset]) # 获取数据集中当前特征的所有值
        new_ent = 0.0
        s = 0.0
        for value in unique_values: # 遍历此特征所有的值
            sub_dataset = split_dataset(dataset, i, value) # 按照这个值,获得子数据集 
            prob = len(sub_dataset) / len(dataset) # 子数据集的长度 就是 此特征i值为value时的数据个数
            new_ent += prob * calc_shannonent(sub_dataset)  # 例:(|)
            s -= prob * log(prob, 2) # 例:_ ()
        gain = ent - new_ent # 计算信息增益
        infogain = gain / s # 计算信息增益率
        if infogain > best_gain: # 如果此信息增益率更大
            best_gain = infogain # 更新
            best_feature_id = i
    return best_feature_id # 返回信息增益率更大的特征编号

'''
5. 创建决策树,算法主函数(以第一次分支为例)
'''
def createTree(dataset, labels):
    class_list = [data[-1] for data in dataset]  # 获取当前数据集每行的分类标签,贷款是/否
    if class_list.count(class_list[0]) == len(class_list):
        # 如果当前数据集中,属于同一类,则退出递归
        return class_list[0] # 返回此类的标签

    # 获取最好的分类特征索引
    best_feature_id = choose_best_feature(dataset) # 编号为2
    # 获取该特征的名字
    best_feature_label = labels[best_feature_id] # 特征名为:车否

    # 这里直接使用字典变量来存储树信息,这对于回执树形图很重要
    my_tree = {best_feature_label:{}}    # 当前数据集选取最好的特征存储在bestFeat中
    del(labels[best_feature_id])    # 删除已经在选取的特征  此时  labels = ['是否有车']
    
    # 取出最优列的值
    feature_values_set = set([data[best_feature_id] for data in dataset]) # ['是', '否']
    for value in feature_values_set: # 对于此特征的每一个取值
        temp = split_dataset(dataset, best_feature_id, value) # 切分数据集,进行分支
        my_tree[best_feature_label][value] = createTree(temp, labels) # 对于每一个子节点递归建树
    return my_tree
'''
6. 输入特征,进行预测
'''
# 决策树算法得到的结果是这种形式的:{'车否': {'否': {'婚否': {'否': '否', '是': '是'}}, '是': '是'}}
def get_class(node, data, step=0): # 递归函数
    if type(node) == str: # 如果当前结点类型为str,而不是dict,说明找到结果了
        return node # 返回结果
    for k1, v1 in node.items(): # 找到特征名称
        for k2, v2 in v1.items(): # 找到决策树中此特征的取值
            if data[k1] == k2: # 如果当前数据中此特征的值等于k2
                res = get_class(v2, data, step+1) # 递归
                if res != None: # 如果找到的结果不是None,则一定是“是”或“否”
                    return res # 直接返回
# 预测主函数
def predict(test_dataset, my_tree, labels):
    print("使用决策树预测:")
    for i in range(len(test_dataset)): # 遍历测试集
        data = {} # 将测试集的每一行数据,做个特征到特征值的映射
        for j in range(len(test_dataset[i])):
            data[labels[j]] = test_dataset[i][j]
        res = get_class(my_tree, data) # 根据决策树,找到结果
        print(test_dataset[i], res) # 打印结果

'''
7. 打印树
'''
def show_result(node, class_label, step=0):
    if type(node) == str:
        print('-'*step + class_label + ":" + node)
        return
    for k1, v1 in node.items():
        for k2, v2 in v1.items():
            print('-'*step + k1 + ':' + k2)
            show_result(v2, class_label, step+1)

dataset, labels, class_label, test_dataset = get_dataset()
tmp_labels = labels[:]
my_tree = createTree(dataset, tmp_labels)
print("打印决策树:")
show_result(my_tree, class_label)
print("---------------------------------")
predict(test_dataset, my_tree, labels)

二、测试

要求:
【数据挖掘】决策树分类C4.5算法(python实现)_第1张图片
结果:
【数据挖掘】决策树分类C4.5算法(python实现)_第2张图片

你可能感兴趣的:(数据挖掘,机器学习)