决策树的生成(该函数是一个递归的过程)CreateTree
输入:数据集、特征
输出:字典型数据——决策树
a、判断是否满足停止划分的条件
若当前数据集的属性值为空,则投票表决当前样本中最多的类别
若当前所有的样本类别相同,则返回当前数据的类别。
b、寻找当前数据的最佳划分特征
c、将最佳特征作为关键字,保存到字典中
d、从当前的属性集合中删除该最佳特征
e、遍历该最佳划分特征的所有属性值feat,循环调用函数 CreateTree(输入参数为:最佳特征值为feat的所有数据集,去除最佳特征的属性集合)
代码注意:
1、生成的决策树用字典保存,并且每个关键字的值是一个字典;
2、生成的决策树可以用 pickle 序列化对象保存;
3、ID3 算法适用于标称型数据,在函数的输入、输出中,数据类型为 listlist。
代码:
#-*- coding:utf-8 -*-
import numpy as np
from numpy import *
import pandas as pd
from math import *
import operator
import pickle # 使用该模块实现对决策树的保存
# 数据导入
def loadData(fileName):
dataSet = []
fr = open(fileName)
for featVector in fr.readlines():
lineVector = featVector.strip().split('\t')
dataSet.append(lineVector)
return dataSet
def calcuEntropy(myData): # 计算信息熵
numSample = len(myData)
myClassCount = {}
for featVector in myData:
theKey = featVector[-1]
if theKey not in myClassCount:
myClassCount[theKey] = 0
myClassCount[theKey] += 1
myEntropy = 0
for Keys in myClassCount.keys():
Px = float(myClassCount[Keys])/numSample
myEntropy -= Px*log(Px,2) # 需要导入 math 库
return myEntropy
# 划分数据集:返回划分好的数据集
def splitDataSet(dataX,FeatureNumber,value): # 输入:数据集、第i 个特征、该属性的值
retMat = []
for featVect in dataX:
if featVect[FeatureNumber] == value:
x1 = featVect[:FeatureNumber]
x2 = featVect[FeatureNumber+1:]
x1.extend(x2)
retMat.append(x1)
#print"retMat", retMat
return retMat
# 计算最优特征:计算每个特征下的信息熵,信息熵最大的既是最优特征,返回的数字 i 代表第 i 个特征
def GetBestFeature(dataM):
BestFeat = -1; LargestInformGain = -1 # 最佳特征、最大信息增益
theEntropy = calcuEntropy(dataM)
FeatNumber = len(dataM[0])-1
for i in range(FeatNumber):
FeatList = [example[i] for example in dataM] # 统计每个特征有几个特征值
FeatUnique = set(FeatList) # 每个特征中的特征值,计算每个特征值下的信息增益
NewEntropy = 0.0
for j in FeatUnique:
retMat = splitDataSet(dataM,i,j) # 得到满足条件的数据
Prob = len(retMat)/float(len(dataM))
NewEntropy -= Prob*calcuEntropy(retMat) # 注意:这里是子数据集的概率x 该数据集的熵
informGain = theEntropy + NewEntropy
if informGain > LargestInformGain:
LargestInformGain = informGain
BestFeat = i
return BestFeat
def RoleOfVote(dataM): # 投票规则:少数服从多数
lables = [example[-1] for example in dataM]
lablesCount = {}
for i in lables:
if i not in lablesCount.keys():
lablesCount[i] = 0
lablesCount[i] += 1
theSort = sorted(lablesCount.iteritems(),key =operator.itemgetter(1),reverse=True)
return theSort[0][0] # 返回出现次数最多的类别标签
# 决策树生成: 首先,判断是否满足停止划分的条件:1、所有的类别标签相同 2、属性值为空
def CreateTree(dataSet,label):
allLabels = [example[-1] for example in dataSet]
#print "调用几次"
if allLabels.count(allLabels[0])==len(allLabels):
return allLabels[0]
if(len(dataSet[0])==1): # 没有属性可以划分时,采用投票规则
return RoleOfVote(dataSet)
featNumber = GetBestFeature(dataSet) # 返回最佳划分的编号
#print featNumber
bestFeature = label[featNumber]
myTree = {bestFeature:{}}
del(label[featNumber])
featValue = [example[featNumber] for example in dataSet]
uniqueFeatValues = set(featValue)
for i in uniqueFeatValues:
subLabels = label[:]
myTree[bestFeature][i] = CreateTree(splitDataSet(dataSet, featNumber, i), subLabels)
return myTree
# 对输入样本进行分类
def SampleClassify(inputTree, featLabels, testVec): # 输入树、属性标签、测试向量
firstStr = inputTree.keys()[0] # 决策树的第一个关键词(第一个划分的属性)
secondDict = inputTree[firstStr] # 决策树每个关键字对应的值也是字典
featIndex = featLabels.index(firstStr) # 得到该特征在属性标签中的编号 K
key = testVec[featIndex] # 得到当前测试向量中第 K 号特征值
valueOfFeat = secondDict[key]
if isinstance(valueOfFeat, dict): # 判断变量是否为字典类型
classLabel = SampleClassify(valueOfFeat, featLabels, testVec)
else:
classLabel = valueOfFeat
return classLabel
# 将生成的树保存
def storeTree(inputTree, filename):
fw = open(filename, 'w')
pickle.dump(inputTree, fw)
fw.close()
# 加载保存的树
def loadTree(filename):
fr = open(filename)
return pickle.load(fr)
if __name__=="__main__":
print "hello world"
dataSet = loadData('lenses.txt')
labels = ['age','prescript','astigmatic','tearRate']
slabels = labels[:] # pyhton 函数中的参数是按照引用方式传递的,为防止labels 改变,复制类标签带入
myTree = CreateTree(dataSet,slabels)
print labels
#storeTree(myTree,"xu") # 保存决策树
test = dataSet[0]
#print test[:-1]
print"预测结果是:", SampleClassify(myTree,labels,test[:-1])
print"真是标签是:",test[-1]
注:
ID3决策树在选择属性时,会找到信息增益最大的划分属性
ID3决策树如果对连续变化的属性进行划分,则需要先将连续值离散化(分区间)