感谢
import os
import math
def createDataSet():
"""
outlook-> 0: sunny | 1: overcast | 2: rain
temperature-> 0: hot | 1: mild | 2: cool
humidity-> 0: high | 1: normal
windy-> 0: false | 1: true
"""
dataSet = [[0, 0, 0, 0, 'N'],
[0, 0, 0, 1, 'N'],
[1, 0, 0, 0, 'Y'],
[2, 1, 0, 0, 'Y'],
[2, 2, 1, 0, 'Y'],
[2, 2, 1, 1, 'N'],
[1, 2, 1, 1, 'Y']]
labels = ['outlook', 'temperature', 'humidity', 'windy']
return dataSet, labels
def calcShannonEnt(dataSet):
"""
输入:数据集
输出:数据集的香农熵
描述:计算给定数据集的香农熵;熵越大,数据集的混乱程度越大
仅仅计算输入的数据集的标签的信息熵,表达了数据集的标签的混乱程度/纯度
"""
num_samples=len(dataSet)
# dataSet 为二维列表,len(dataSet)表示数据集中共有多少个训练样本
# len(dataSet[0]) 表示训练样本中共包含多少维度的特征
label_dict={}
for i in range(num_samples):
if dataSet[i][-1] not in label_dict:
# print(dataSet[i][-1])
label_dict[dataSet[i][-1]]=1
else:
label_dict[dataSet[i][-1]]+= 1
shannonEnt=0.0
for k,v in label_dict.items():
shannonEnt+=-(v/num_samples)*math.log(v/num_samples,2)
return shannonEnt
def chooseBestFeature(dataSet):
'''
:param dataSet: 对于输入的数据集求解最优的属性划分
:return:
'''
num_features=len(dataSet[0])
num_samples=len(dataSet)
entropy=calcShannonEnt(dataSet)
# 计算数据集的经验熵
# 计算每个属性的条件熵
condition_entropy=[0]*num_features
for i in range(num_features):
# 给出当前特征属性的所有取值
temp_dict={}
temp_line_dict={}
# print('here features',i)
for j in range(num_samples):
if dataSet[j][i]==float('inf'):
# 意味着实际上当前属性是无效的
condition_entropy[i]=float('inf')
break
if dataSet[j][i] not in temp_dict:
temp_dict[dataSet[j][i]]=1
temp_line_dict[dataSet[j][i]]=[dataSet[j][:i]+dataSet[j][(i+1):]]
else:
temp_dict[dataSet[j][i]]+= 1
temp_line_dict[dataSet[j][i]].append(dataSet[j][:i] + dataSet[j][(i + 1):])
for k,v in temp_dict.items():
# print(temp_line_dict[k])
condition_entropy[i]+=(v/num_samples)*calcShannonEnt(temp_line_dict[k])
# 选择信息增益最大的特征维度作为当前的划分属性
compare=list(map(lambda x:entropy-x,condition_entropy))
best=-100
best_index=-1
for index in range(num_features):
if compare[index]>best:
best=compare[index]
best_index=index
return best_index
def split_dataset(dataSet,index,value):
'''
:param dataSet: 待划分的数据集
:param index: 根据信息增益最大的原则,求出来的将数据集进行最优化分的属性索引值,index从0开始
:param value: 将当前的属性索引处取值为value的样本都取出来,构成划分后的数据集,此时将已知的属性维度数值变成负无穷
:return: 划分后的数据集
'''
output=[]
num_samples=len(dataSet)
for i in range(num_samples):
if dataSet[i][index]==value:
dataSet[i][index]=float('inf')
output.append(dataSet[i])
return output
def major_out(dataSet):
label_dict={}
num_samples=len(dataSet)
for i in range(num_samples):
if dataSet[i][-1] not in label_dict:
label_dict[dataSet[i][-1]]=1
else:
label_dict[dataSet[i][-1]] = 1
max_num=-100
major_label=''
for k,v in label_dict.items():
if v>max_num:
max_num=v
major_label=k
return major_label
def buildTree(dataSet, labels):
# 构建决策树的最终目的是为了给输入的数据分类一个标签,故而决策树的输出值是标签
# print(dataSet)
# 将决策树表示成嵌套字典的形式
class_list=[sample[-1] for sample in dataSet]
if len(list(set(class_list)))==1:
# 如果当前的数据集中只有一个类别,则就将该类别输出
return class_list[0]
if len(list(set(dataSet[0])))==1:
# 如果数据表中没有其他属性可以考虑, 则N也是树叶,按
# 照少数服从多数的原则在树叶上标出所属类别
return major_out(dataSet)
# 否则,根据平均信息期望值E或GAIN值选出一个最佳属性
# 作为节点N的测试属性,得到最佳属性的索引下标
best_feat=chooseBestFeature(dataSet)
feat_value=list(set([line[best_feat] for line in dataSet]))
# {'outlook': {0: 'N', 1: 'Y', 2: {'windy': {0: 'Y', 1: 'N'}}}}
# 构建多重字典以存储决策树
myTree={labels[best_feat]:{}}
for v in feat_value:
# print('v',v)
sub_dataset=split_dataset(dataSet,best_feat,v)
# print('sub_dataset',sub_dataset)
if sub_dataset is not None:
myTree[labels[best_feat]][v]=buildTree(sub_dataset,labels)
return myTree
def createTestSet():
"""
outlook-> 0: sunny | 1: overcast | 2: rain
temperature-> 0: hot | 1: mild | 2: cool
humidity-> 0: high | 1: normal
windy-> 0: false | 1: true
"""
testSet = [[0, 1, 0, 0],
[0, 2, 1, 0],
[2, 1, 1, 0],
[0, 1, 1, 1],
[1, 1, 0, 1],
[1, 0, 1, 0],
[2, 1, 0, 1]]
return testSet
def class_one_sample(test_sample,labels,Tree):
temp_tree = Tree.copy()
while (1):
for k, v in temp_tree.items():
temp_tree = temp_tree[k][
test_sample[labels.index(k)]] # {0: 'N', 1: 'Y', 2: {'windy': {0: 'Y', 1: 'N'}}}
if isinstance(temp_tree, str):
return temp_tree
def classify(testSet,labels,Tree):
'''
:param testSet: 测试数据集
:param Tree: 根据训练数据集构建出来的决策树
{'outlook': {0: 'N', 1: 'Y', 2: {'windy': {0: 'Y', 1: 'N'}}}}
:param labels: 特征属性的名称
:return:
'''
num_samples=len(testSet)
num_feat=len(labels)
output=[0 for _ in range(num_samples)]
for i in range(num_samples):
output[i]=class_one_sample(testSet[i],labels,Tree)
return output
if __name__=='__main__':
dataSet, labels=createDataSet()
tree=buildTree(dataSet,labels)
print(tree)
# {'outlook': {0: 'N', 1: 'Y', 2: {'windy': {0: 'Y', 1: 'N'}}}}
# print(calcShannonEnt(dataSet))
# print(chooseBestFeature(dataSet))
testSet=createTestSet()
print(classify(testSet,labels,tree))
# ['N', 'N', 'Y', 'N', 'Y', 'Y', 'N']