有用请点赞,没用请差评。
欢迎分享本文,转载请保留出处。
本次代码是基于上一节决策树ID3\C45修改过来的,建议两篇博客一起看。具体算法原理等有时间了再写。
# -*- coding:utf-8 -*-
# Decision tree by cart决策树,cart算法,算法参考李航《统计学习方法》P71
#author:Tomator
import numpy as np
import math
from sklearn.model_selection import train_test_split
# 测试数据集,《统计学习方法》P59,贷款申请样本数据集
def createDataSet():
dataSet = [[0, 0, 0, 0, 'no'], # 数据集
[0, 0, 0, 1, 'no'],
[0, 1, 0, 1, 'yes'],
[0, 1, 1, 0, 'yes'],
[0, 0, 0, 0, 'no'],
[1, 0, 0, 0, 'no'],
[1, 0, 0, 1, 'no'],
[1, 1, 1, 1, 'yes'],
[1, 0, 1, 2, 'yes'],
[1, 0, 1, 2, 'yes'],
[2, 0, 1, 2, 'yes'],
[2, 0, 1, 1, 'yes'],
[2, 1, 0, 1, 'yes'],
[2, 1, 0, 2, 'yes'],
[2, 0, 0, 0, 'no']]
labels = ['年龄', '有工作', '有自己的房子', '信贷情况'] # 分类属性
return dataSet, labels # 返回数据集和分类属性
# 计算基尼指数
def cal_gini(data_vector):
nums_data = len(data_vector) # 数据集样本数
counts_by_labels = {} # 用来保存每个label下的样本数
gini = 0 #基尼指数
p_sum=0 #每个类别的样本数
for vector in data_vector:
if vector[-1] not in counts_by_labels: # vector[-1]为label值
counts_by_labels[vector[-1]] = 0
counts_by_labels[vector[-1]] += 1 # 统计label出现的次数
for key in counts_by_labels:
p = float(counts_by_labels[key] / nums_data) # 计算每个标签出现的概率
p_sum+= p**2
gini=1-p_sum # 公式5.24
return gini
# 返回类列表中出现次数最多的类标签
def max_class(label_list):
count_label = {}
for label in label_list:
if label not in count_label:
count_label[label] = 0
count_label[label] += 1
# 选择字典value最大的所对应的key值
return max(count_label, key=count_label.get)
"""
根据每个特征划分数据集
data_vector
index_feature:特征的索引位置i
value:用来划分的特征取值
返回划分后的子数据及样本数,和子数据集(子数据集剔除了第i列特征)
"""
# 根据cart算法划分数据集,cart算法生成的是二叉数,因此分割之后也就只有两个子数据集。返回分割后的D1和D2数据集
def split_datatset_cart(data_vector, index_feature, value):
split_set_yes = [] #判别为“是”的子数据集
split_set_no=[] #判别为“否”的子数据集
for vector in data_vector:
if vector[index_feature] == value:
# 去掉第i列特征
split_1 = vector[:index_feature]
split_1.extend(vector[index_feature + 1:])
split_set_yes.append(split_1)
else:
split_2 = vector[:index_feature]
split_2.extend(vector[index_feature + 1:])
split_set_no.append(split_2)
# 分别输出D1和D2数据集以及对应的数据集样本数
return len(split_set_yes),split_set_yes,len(split_set_no),split_set_no
# 选择最优分类特征
# 生成决策树的方法:cart算法
def choose_bestfeture_cart(data_vector):
nums_data = len(data_vector)
nums_feature = len(data_vector[0]) - 1 # 每个样本所包含的特征个数
min_gini = float('inf') # 表示最小的基尼指数
best_index_feature = 0 # 表示最优特征的索引位置index
best_split_point=None #表示最优的切分点
for i in range(nums_feature): # 遍历所有的特征
features_i_set = [vector[i] for vector in data_vector] # 提取第i个特征中所包含的可能取值
features_i_set = list(set(features_i_set)) # 对特征值去重
feature_gini = 0 #每个特征中每个特征值所对应的基尼指数
# 如果第i个特征向量包含的特征取值个数小于2,则只有一个切分点。参考P71例5.4
if len(features_i_set)<=2:
# 同时选取features_i_set中数值最大特征取值为切分点。当然选取最小取值也有一样的结果,这只是设定的一个规矩。
# fea为切分点
fea=max(features_i_set)
# 根据切分点进行划分子数据集
nums_di_yes, di_set_yes, nums_di_no, di_set_no = split_datatset_cart(data_vector, i, fea) #
p_di_yes = nums_di_yes / nums_data # 计算|Di|/|D|
gini_yes_di = cal_gini(di_set_yes) # 计算yes子类的gini指数
feature_gini += p_di_yes * gini_yes_di
p_di_no = nums_di_no / nums_data
gini_yes_no = cal_gini(di_set_no) # 计算no子类的gini指数
feature_gini += p_di_no * gini_yes_no
# 选取最优的分类特征和最优切分点
if feature_gini < min_gini:
min_gini = feature_gini
best_index_feature = i
best_split_point = fea
# 如果第i个特征向量包含的特征取值个数小于2,则有多个切分点
else:
for fea in features_i_set: # 遍历第i个特征的所有vlaue
nums_di_yes, di_set_yes,nums_di_no, di_set_no = split_datatset_cart(data_vector, i, fea) #
p_di_yes = nums_di_yes / nums_data # 计算|Di|/|D|
gini_yes_di = cal_gini(di_set_yes) # 计算yes子类的gini指数
feature_gini += p_di_yes * gini_yes_di
p_di_no=nums_di_no/nums_data
gini_yes_no=cal_gini(di_set_no)
feature_gini += p_di_no*gini_yes_no
# 选取最优的分类特征和最优切分点
if feature_gini