基于周志华西⽠数据集的决策树算法及准确率测试
基于周志华西⽠数据集的决策树算法及准确率测试
1.决策树介绍
举个通俗的栗⼦来解释⼀下什么是决策树,想象⼀个⼥孩的母亲要给这个⼥孩介绍男朋友:
⼥⼉:有没有房⼦?母亲:有。
⼥⼉:长的帅不帅?母亲:挺帅的。
⼥⼉:收⼊⾼不?
母亲:不算很⾼,中等情况。
⼥⼉:是公务员不?母亲:是,在税务局上班呢。
⼥⼉:那好,我去见见。
这个⼥孩的决策过程就是典型的分类树决策。相当于通过是否有房、长相、收⼊和是否公务员对将男⼈分为两个类别:见和不见。下⾯我们通过流程图把⼥⼉的决策树判断过程展现出来:
通过这个例⼦,⼤家已经对决策树算法有个基本了解了吧,这也是决策树算法的⼀⼤优势——数据形式⾮常容易理解。
2.⽤python构造决策树基本流程
杨丞琳左边下图是西⽠书中的决策树学习基本算法,接下来我们将根据这个算法流程⽤python代码⾃⼰写⼀棵决策树。
在构造决策树时,要解决的第⼀个问题就是,当前数据集哪个特征在划分数据分类时起决定性作⽤。在前⾯相亲的例⼦中,⼥孩为何第⼀个问题是“是否有房⼦”呢,因为是否有房⼦这个特征能够提供的“信息量”很⼤,划分选择就是找提供“信息量”最⼤的特征,学术上叫信息增益。
3.划分选择(按照信息增益)
什么是信息增益呢,官⽅介绍请参考西⽠书哈,个⼈认为就是⼀个信息提纯的过程,⽐如⼀堆黄⾖和⼀堆红⾖混在⼀起,这时候信息的纯度是很低的,如果我们把红⾖挑出来了分成两堆,那这时候纯度就⾼了。这就是⼀个信息增益的过程,衡量信息纯度的标准,就是信息熵。
信息熵是度量样本集合纯度最常⽤的⼀种指标,我的个⼈理解是对⼀个事件进⾏编码,所需要的平均码长就是信息熵,纯度越⾼,需要的平均代码就越短,信息熵越低。
import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
import collections
#计算给定数据集的⾹浓熵
from math import log
def splitDataSet(dataSet, index, feature):
splitedDataSet =[]
mD =len(dataSet)
for data in dataSet:
if(data[index]== feature):
sliceTmp = data[:index]
splitedDataSet.append(sliceTmp)
return splitedDataSet
def Ent(datat):
n =len(datat)
label_counts ={}
for item in datat:#遍历数据集
label_current = item[-1]#存⼊
if label_current not in label_counts.keys():
label_counts[label_current]=0#将特征值存⼊,并标记为0
label_counts[label_current]+=1
ent =0.0
for key in label_counts:
prob = label_counts[key]/n
ent -= prob * log(prob,2)
return ent
return ent
#测试我们编写的⾹浓熵计算函数
data = pd.read_csv('xigua1.csv',encoding='gbk')
print(data)
#ad_csv('textSet.csv')
#print(test)
#a=Ent(data.iloc[:,-1])#取数据集最后⼀列
#按照权重计算各分⽀的信息熵
def sum_weight(grouped,total_len):
weight =len(grouped)/total_len
return weight * Ent(grouped.iloc[:,-1])
#根据公式计算信息增益
def Gain(column, data):
lenth =len(data)
ent_sum = upby(column).apply(lambda x:sum_weight(x,lenth)).sum()#按照column重新排列,然后计算信息熵,再加⼀块☆!!#print("11",ent_sum)
ent_D = Ent(data.iloc[:,-1])
#print("22",ent_D)
return ent_D - ent_sum
#计算按照属性'⾊泽'的信息增益
# 计算获取最⼤的信息增益的feature,输⼊data是⼀个dataframe,返回是⼀个字符串高级海员
def get_max_gain(data):
max_gain =0.0
cols = lumns[:-1]
for col in cols:
gain = Gain(col,data)
#print(gain)
if gain > max_gain:
max_gain = gain
max_label = col李克勤我是歌手
return max_label
#获取data中最多的类别作为节点分类,输⼊⼀个ries,返回⼀个索引值,为字符串
def get_most_label(label_list):
return label_list.value_counts().idxmax()#value_counts:指数据集中值有哪些,每个出现多少次
# 创建决策树,传⼊的是⼀个dataframe,最后⼀列为label
def TreeGenerate(data):
feature = lumns[:-1]
label_list = data.iloc[:,-1]
#如果样本全属于同⼀类别C,将此节点标记为C类叶节点产品策略有哪些
if len(pd.unique(label_list))==1:
return label_list.values[0]
#如果待划分的属性集A为空,或者样本在属性A上取值相同,则把该节点作为叶节点,并标记为样本数最多的分类
elif len(feature)==0or len(data.loc[:,feature].drop_duplicates())==1:
return get_most_label(label_list)
#从A中选择最优划分属性
best_attr = get_max_gain(data)
tree ={best_attr:{}}
#对于最优划分属性的每个属性值,⽣成⼀个分⽀
for attr,gb_data upby(by=best_attr):
if len(gb_data)==0:
tree[best_attr][attr]= get_most_label(label_list)
el:
#在data中去掉已划分的属性
#在data中去掉已划分的属性
new_data = gb_data.drop(best_attr,axis=1)
#递归构造决策树
tree[best_attr][attr]= TreeGenerate(new_data)
return tree
#使⽤递归函数进⾏分类
def tree_predict(tree, data):
#print(data)
feature =list(tree.keys())[0]#取树第⼀个结点的键(特征)
#print(feature)
label = data[feature]#该特征下所有属性
next_tree = tree[feature][label]#下⼀个结点树
江西本科大学排名if type(next_tree)==str:#如果是个字符串
return next_tree
el:
return tree_predict(next_tree, data)
#切割训练集和测试集
# 训练模型
ics import accuracy_score
del_lection import train_test_split
#切割训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(data.iloc[:,:-1], data.iloc[:,-1], test_size =0.3, random_state=1) train = pd.concat([X_train,y_train],axis=1)
print("train",X_train)
print("test",y_test)
decition_tree = TreeGenerate(train)
print(decition_tree)
y_predict = X_test.apply(lambda x:tree_predict(decition_tree, x),axis=1)
score = accuracy_score(y_test,y_predict)
普法小故事
print('第实验准确率为:'+repr(score*100)+'%')
其实上⾯算法是有缺陷的,有可能缺失分⽀,需要补全分⽀:
import numpy as np
import pandas as pd
import random
import csv
红萝卜炒肉丝ics import accuracy_score
del_lection import train_test_split
#计算熵
def calcEntropy(dataSet):
mD =len(dataSet)
dataLabelList =[x[-1]for x in dataSet]
dataLabelSet =t(dataLabelList)
ent =0
for label in dataLabelSet:
mDv = unt(label)
prop =float(mDv)/ mD
ent = ent - prop * np.math.log(prop,2)
ent = ent - prop * np.math.log(prop,2)
return ent
# # 拆分数据集
# # index - 要拆分的特征的下标
# # feature - 要拆分的特征
# # 返回值 - dataSet中index所在特征为feature,且去掉index⼀列的集合
def splitDataSet(dataSet, index, feature):
splitedDataSet =[]
mD =len(dataSet)
for data in dataSet:
if(data[index]== feature):
sliceTmp = data[:index]
splitedDataSet.append(sliceTmp)
return splitedDataSet
#根据信息增益 - 选择最好的特征
# 返回值 - 最好的特征的下标
def chooBestFeature(dataSet):
汤成语
entD = calcEntropy(dataSet)
mD =len(dataSet)
featureNumber =len(dataSet[0])-1
maxGain =-100
maxIndex =-1
for i in range(featureNumber):
entDCopy = entD
featureI =[x[i]for x in dataSet]
featureSet =t(featureI)
for feature in featureSet:
splitedDataSet = splitDataSet(dataSet, i, feature)# 拆分数据集
mDv =len(splitedDataSet)
entDCopy = entDCopy -float(mDv)/ mD * calcEntropy(splitedDataSet) if(maxIndex ==-1):
maxGain = entDCopy
maxIndex = i
elif(maxGain < entDCopy):
maxGain = entDCopy
maxIndex = i
return maxIndex
# 寻找最多的,作为标签
def mainLabel(labelList):
labelRec = labelList[0]
maxLabelCount =-1
labelSet =t(labelList)
for label in labelSet:
unt(label)> maxLabelCount):
maxLabelCount = unt(label)
labelRec = label
return labelRec
#⽣成决策树
# featureNamesSet 是featureNames取值的集合
# labelListParent 是⽗节点的标签列表
def createDecisionTree(dataSet, featureNames):
labelList =[x[-1]for x in dataSet]
if(len(dataSet[0])==1):#没有可划分的属性了
return mainLabel(labelList)#选出最多的label作为该数据集的标签
unt(labelList[0])==len(labelList)):# 全部都属于同⼀个Label return labelList[0]
bestFeatureIndex = chooBestFeature(dataSet)
bestFeatureName = featureNames.pop(bestFeatureIndex)