from math import log
import operator
'''
创建决策树进行分类的流程如下:
1.创建数据集
2.计算数据集的信息熵 !!
3.遍历所有特征,选择信息熵最小的特征,即为最好的分类特征 !!
4.根据上一步得到的分类特征分割数据集,并将该特征从列表中移除
5.执行递归函数,返回第三步,不断分割数据集,直到分类结束 !!
6.使用决策树执行分类,返回分类结果
'''
# 创建海洋生物数据集,判断是否是鱼类
my_labels = ['no surfacing', 'flippers'] # 是否浮出水面,是否有脚蹼
my_data = [
[1, 1, 'yes'],
[1, 1, 'yes'],
[1, 0, 'no'],
[0, 1, 'no'],
[0, 1, 'no']
]
# 计算给定数据集的香农熵(信息熵)
def calc_shannon_entropy(dataset):
# 实例总数 entry (计算机)事项
num_entries = len(dataset)
# 为所有可能分类创建字典,计算每一类的出现频数
label_counts = {}
for fv in dataset:
label = fv[-1] # 特征向量最后一个数据是label
if label not in label_counts.keys():
label_counts[label] = 0 # 如果不在,添加新label,并设为0
label_counts[label] += 1 # 如果已在,老label+1
# 计算熵
shannon_entropy = 0.0
for key in label_counts:
prob = float(label_counts[key]) / num_entries # 选择每一类的概率
shannon_entropy -= prob * log(prob, 2)
return shannon_entropy
# 划分数据集 执行一次得到一个子集
# axis: 用来划分数据的 特征
# value: 需要返回的特征值
def split_dataset(dataset, axis, value):
ret_dataset = []
for fv in dataset:
if fv[axis] == value:
reduced_fv = fv[:axis] # 去掉 axis 表示的特征 去掉 fv[axis] 减1维
reduced_fv.extend(fv[axis + 1:])
ret_dataset.append(reduced_fv) # 分割后的 fv 加入数据集
return ret_dataset
# 选择最好的数据集划分方式,返回 最佳特征 所在位置
def choose_best_feature(dataset):
base_entropy = calc_shannon_entropy(dataset) # 信息熵
base_info_gain = 0.0 # 信息增益
base_feature = -1
num_features = len(dataset[0]) - 1 # 减去最后一个label
for i in range(num_features):
# 以 fv[i] 分割数据集
feature_list = [fv[i] for fv in dataset] # 所有样本的 第i维特征值 组成的list
unique_vals = set(feature_list) # 去掉相同项
# 计算熵变
new_entropy = 0.0
for val in unique_vals:
sub_dataset = split_dataset(dataset, axis=i, value=val) # 第i个特征 = 不同的val
prob = len(sub_dataset) / len(dataset) # 第i维=val的比例
new_entropy += prob * calc_shannon_entropy(sub_dataset) # 子集的熵
# print(i, new_entropy)
info_gain = base_entropy - new_entropy
# 熵变越大,new_entropy 越小,剩下的混乱度越小
if info_gain > base_info_gain:
base_info_gain = info_gain
base_feature = i
return base_feature
# 如果数据集已经使用了所有特征向量属性,最后的类标签还不是唯一的
# 此时通过多数表决的方式 决定叶子节点的分类 比如 yes:no = 3:2 则认为该叶子节点是 yes
# class_list 是 一系列 labels 要统计每一类的数量
def majority_cnt(class_list):
# 统计每一项 label 的 example 数量
class_cnt = {}
for vote in class_list:
if vote not in class_cnt.keys():
class_cnt[vote] = 0
class_cnt[vote] += 1
# 从大到小排序
sorted_class_cnt = sorted(class_cnt.items(), key=operator.itemgetter(1), reverse=True)
return sorted_class_cnt[0][0]
# 递归创建树
# labels 不是必要的,但是可以使树的结构清晰,每一次分割所用的特征很清楚地表示在字典的 key 里
def create_tree(dataset, labels):
# 如果 dataset 中实例的类标签都相同,递归结束
class_list = [fv[-1] for fv in dataset]
if class_list.count(class_list[0]) == len(class_list): # 说明class_list中只有一项
return class_list[0] # 返回label
# 如果使用完了所有特征,递归结束
# 因为这一情况下,还可能存在不同的类标签,所以使用多数表决
if len(dataset[0]) == 1: # 划分的 fv 只剩下最后一维: label
return majority_cnt(class_list)
# 得到当前分割的 最佳 特征
best_feature = choose_best_feature(dataset)
best_feature_label = labels[best_feature]
# 字典类型
tree = {
best_feature_label: {}
}
# 特征集中去掉 这一特征
labels.remove(best_feature_label)
feature_vals = [fv[best_feature] for fv in dataset]
unique_vals = set(feature_vals)
for val in unique_vals:
sub_labels = labels[:] # 拷贝一份labels给一个递归位置使用,避免全局的labels可能在不同的递归位置出错
tree[best_feature_label][val] = create_tree(split_dataset(dataset, axis=best_feature, value=val), sub_labels)
return tree
# 使用决策树返回分类结果
def classify(tree, labels, test_vec):
first_feature = list(tree.keys())[0]
second_dict = tree[first_feature]
label_index = labels.index(first_feature)
class_label = 'null'
for key in second_dict.keys():
if test_vec[label_index] == key:
if type(second_dict[key]).__name__ == 'dict':
class_label = classify(second_dict[key], labels, test_vec)
else:
class_label = second_dict[key]
return class_label
# 1.测试熵
def test_entropy():
print(calc_shannon_entropy(my_data)) # 0.9709505944546686
# 熵越高,说明混乱度越大,混合的数据越多
# 修改第一个数据项,增加1个分类
my_data[0][-1] = 'maybe'
print(calc_shannon_entropy(my_data)) # 1.3709505944546687
# 继续增加分类
my_data[3][-1] = 'perhaps'
print(calc_shannon_entropy(my_data)) # 1.9219280948873623
# 全改成同一类
for fv in my_data:
fv[-1] = 'yes'
print(calc_shannon_entropy(my_data)) # 0.0
# 2.测试划分数据集
def test_split():
# 按照 fv[0] 划分数据集
sub_dataset_axis01 = split_dataset(my_data, axis=0, value=1) # 返回 fv[0]==1 的
sub_dataset_axis00 = split_dataset(my_data, axis=0, value=0) # 返回 fv[0]==0 的
print(sub_dataset_axis01)
print(sub_dataset_axis00)
# 3.测试获取最佳特征
def test_best_fv():
print(choose_best_feature(my_data))
if __name__ == '__main__':
tmp_labels = my_labels[:] # create_tree 函数会改变 my_labels,所以存一份拷贝
my_tree = create_tree(my_data, tmp_labels)
fvs = [fv[:-1] for fv in my_data]
for fv in fvs:
print(fv, classify(my_tree, my_labels, fv))
Python 决策树
©著作权归作者所有,转载或内容合作请联系作者
- 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
- 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
- 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
推荐阅读更多精彩内容
- 数据源:sales_data.xls 代码结果: 源代码 参考资料:《Python数据分析与挖掘实战》
- Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
- 公安部近日公布近期高发的十类电信网络新型违法犯罪手段,江西舜鑫资产管理有限公司提醒社会公众谨防上当受骗。 十大骗术...