PlayTennis.txt
利用打网球数据集PlayTenis构建决策树,该数据集的特性如下:
属性包括天气(outlook)、温度(temperature)、湿度(humidity)、是否有风(windy),样本个数为14。
标签为今天是否去打网球(play)。
具体数据如下(如果不能运行,尝试在末尾加回车)
1 sunny hot high FALSE no
2 sunny hot high TRUE no
3 overcast hot high FALSE yes
4 rainy mild high FALSE yes
5 rainy cool normal FALSE yes
6 rainy cool normal TRUE no
7 overcast cool normal TRUE yes
8 sunny mild high FALSE no
9 sunny cool normal FALSE yes
10 rainy mild normal FALSE yes
import math
def xiangtong(D, A): # D中所有A类元素是否相同
for i in A: # 循环看D中所有A类元素
if len(set(D[i])) > 1: # 如果里面节点类型不一样
return False # 返回False
return True # 返回True
def deDv(D, a, v): # 得到Dv
Dv = [[], [], [], [], []]
for i in range(len(D[a])): # 属性a列遍历
if D[a][i] == v: # 找到属性v
for j in range(len(D)): # D属性遍历
Dv[j].append(D[j][i]) # D的属性加到Dv上
return Dv # 返回Dv
def Ent(D): # Ent(D) 信息熵
x = list(set(D[4])) # 取得无重复元素的列表
x = x[0] # D中样本标签第一类
c = 0 # 计数
for j in D[4]: # 遍历标签
if j == x: # 如果标签一样
c = c + 1 # 计数加一
pk1 = c / len(D[4]) # 第一类标签的频率
if pk1 == 0 or pk1 == 1: # 如果有0(防止log 0)
return 0 # 信息熵为 0
pk2 = 1 - c / len(D[4]) # 第二类标签的频率
return -(pk1 * math.log(pk1, 2) + pk2 * math.log(pk2, 2)) # 只有两种标签
def Gain(D, i): # 求信息增益
sum = 0 # 划分后信息熵(Ent求和)
for j in set(D[i]): # 遍历D[i]的不同属性
Dv = deDv(D, i, j) # 得到Dv
sum = sum + len(Dv[0]) / len(D[0]) * Ent(Dv) # 求划分后信息熵
return Ent(D) - sum # 信息增益:初始信息熵减去划分后信息熵
def TreeGenerate(D, A): # 递归生成决策树
y = [] # 生成节点
if len(set(D[4])) == 1: # 如果里面节点类型一样
return D[4][0] # 返回这个值
if len(A) == 0 or xiangtong(D, A): # 如果A为空或者D中所有A类元素相同
return max(D[4], key=D[4].count) # 返回D中最多的取值
G = [] # 以属性A分类的信息增益
for i in A:
G.append(Gain(D, i)) # 计算所有的信息增益
G = G.index(max(G)) # 得到a*位置在G中的下标(最优划分属性)
G = A[G] # 得到a*位置在D中的下标
y.append(G)
for i in set(D[G]): # 最优划分属性每个取值
y1 = [i] # 生成分支
Dv = deDv(D, G, i) # 得到Dv
if len(Dv) == 0: # Dv为空
y1.append(max(D[4], key=D[4].count)) # D中最多的取值
else:
A1 = A[:]
A1.remove(G) # 产生去除a*的A
y1.append(TreeGenerate(Dv[:], A1[:])) # 递归调用
y.append(y1)
return y
def panduan(D, y, i):
if isinstance(y, str): # 如果是字符串,说明是结果
return y # 返回结果
if isinstance(y[0], int): # 如果是数字,说明是分类列
x = D[y[0]] # 取分类列
for j in range(1, len(y)): # 遍历这一分类变量所有类型
y1 = y[j] # 取分支
if x[i] == y1[0]: # 找取到该分类
return panduan(D, y1[1], i) # 递归分支
def wucha(D, y): # 计算分类误差
c = 0 # 误差数
for i in range(len(D[0])):
if not panduan(D, y, i) == D[4][i]: # 决策树判断不等于标签
c = c + 1 # 误差数加一
return c / len(D[0]) #返回误差比例
f = open('PlayTennis.txt', 'r') # 读文件
x = [[], [], [], [], []] # 天气、温度、湿度、是否有风、打球
x1 = [[], [], [], [], []]
x2 = [[], [], [], [], []]
y = [] # 分支节点[分支类,[分支节点1],[分支节点2]···] 递归决策树
y1 = []
while 1:
yihang = f.readline() # 读一行
if len(yihang) <= 1: # 读到末尾结束
break
fenkai = yihang.split(' ') # 按空格分开
fenkai = sorted(set(fenkai), key=fenkai.index) # 去除重复元素
for i in range(5):
x[i].append(fenkai[i + 2]) # 加到x中
print('数据集===============================================')
for i in range(len(x)):
print(x[i])
x1 = x[:]
A = [0, 1, 2, 3] # 属性
print('全训练决策树===============================================')
y = TreeGenerate(x1[:], A[:]) # 开始训练
print(y, '\n拆分:')
for i in range(len(y)): # 输出决策树
print(y[i])
print('误差率:', wucha(x[:], y[:]) * 100, '%')
print('2/3训练决策树==============================================')
for i in range(len(x1)): # 截取部分训练集
x2[i] = x1[i][6:9]
x1[i] = x1[i][0:6]
y1 = TreeGenerate(x1, A[:]) # 开始训练
print(y1, '\n拆分:')
for i in range(len(y1)): # 输出决策树
print(y1[i])
print('误差率:', wucha(x2, y1[:]) * 100, '%')
此程序全部做训练集生成的决策树手工绘制为: