1.决策树
k-近邻算法可以完成很多分类任务,但是它最大的缺点就是无法给出数据的内在含义,决策树的主要优势就在于数据形式非常容易理解。
优点:计算复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可以处理不相关特征数据。
缺点:可能会产生过度匹配问题。
适用数据类型:数值型和标称型。
2.信息增益
划分数据集的大原则是:将无序的数据变得更加有序。
在划分数据集之前之后信息发生的变化称为信息增益,知道如何计算信息增益,我们就可以计算每个特征值划分数据集获得的信息增益,获得信息增益最高的特征就是最好的选择。
集合信息的度量方式称为香农熵或者熵。
4.熵
熵定义为信息的期望值。
如果待分类的事务可能划分在多个分类之中,则符号xi的信息定义为:
(其中p(xi)是选择该分类的概率)
为了计算熵,我们需要计算所有类别所有可能值包含的信息期望值,
(其中n为分类的数目)
5.划分数据集
分类算法除了需要测量信息熵,还需要划分数据集,度量划分数据集的熵,以便判断当前是否正确地划分了数据集。
我们将对每个特征划分数据集的结果计算一次信息熵,然后判断按照哪个特征划分数据集是最好的划分方式。
6.代码示例
# coding:utf-8
from math import log
# 计算数据集的熵,数据集的格式为[[],[]],其中dataSet[i][-1]为类型
def calcShannonEnt(dataSet):
numEntries = len(dataSet)
labelCounts = {}
for featVec in dataSet:
currentLabel = featVec[-1]
if currentLabel not in labelCounts.keys():
labelCounts[currentLabel] = 0
labelCounts[currentLabel] += 1
shannonEnt = 0.0
for key in labelCounts:
prob = labelCounts[key]/numEntries*1.0
# print(-log(prob, 2), prob)
shannonEnt -= prob * log(prob, 2) # 这里是信息期望值(熵)的计算公式
return shannonEnt
# 生成数据集
def createDataSet():
dataSet = [[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]
labels = ['no surfacing', 'flippers']
return dataSet, labels
dataSet, labels = createDataSet()
# shannonEnt = calcShannonEnt(dataSet)
# print(shannonEnt)
# 将原数据集按索引和类别返回划分数据集
def splitDataSet(dataset, axis, value):
retdataset = []
for featvet in dataset:
if featvet[axis] == value:
reducedfeatvet = featvet[:axis]
reducedfeatvet.extend(featvet[axis+1:])
retdataset.append(reducedfeatvet)
# print(retdataset)
return retdataset
# splitDataSet(dataSet, 0, 1)
def chooseBestfeatureTosplit(dataset):
numfeatures = len(dataset[0]) - 1
baseEntropy = calcShannonEnt(dataset)
bestinfogain = 0.0
bestfeature = -1
for i in range(numfeatures):
featlist = [example[i] for example in dataset]
uniquevals = set(featlist) # 划分数据集的类别的集合
newEntropy = 0.0 # 计算每个类型的熵
for value in uniquevals:
subdataset = splitDataSet(dataset, i, value)
prob = len(subdataset)/float(len(dataset))
newEntropy += prob * calcShannonEnt(subdataset)
print(subdataset, uniquevals, value, i)
infogain = baseEntropy - newEntropy
if infogain > bestinfogain:
bestinfogain = infogain
bestfeature = i # 获取熵最小的类型,作为此级划分的首选项
# print(i, newEntropy, uniquevals) # 因为可以找到类型最纯的一个划分数据集
# print(bestfeature)
return bestfeature
chooseBestfeatureTosplit(dataSet)
# 代码运行的结果告诉我们,第0个特征是最好的用于划分数据集的特征。
7.递归构建决策树
上面的内容为从数据集构造决策树算法所需的子功能的模块,其工作原理是:得到原始数据集,然后基于最好的属性值划分数据集,由于特征值可能多于两个,因此可能存在大于两个分支的数据集划分。第一次划分之后,数据将被向下传递到树分支的下一个节点,在这个节点上,我们可以再次划分数据。因此我们可以采用递归的原则处理数据集。
代码示例,
# coding:utf-8 from math import log # 计算数据集的熵,数据集的格式为[[],[]],其中dataSet[i][-1]为类型 def calcShannonEnt(dataSet): numEntries = len(dataSet) labelCounts = {} for featVec in dataSet: currentLabel = featVec[-1] if currentLabel not in labelCounts.keys(): labelCounts[currentLabel] = 0 labelCounts[currentLabel] += 1 shannonEnt = 0.0 for key in labelCounts: prob = labelCounts[key]/numEntries*1.0 # print(-log(prob, 2), prob) shannonEnt -= prob * log(prob, 2) # 这里是信息期望值(熵)的计算公式 return shannonEnt # 生成数据集、特征 def createDataSet(): dataSet = [[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']] labels = ['no surfacing', 'flippers'] return dataSet, labels dataSet, labels = createDataSet() # shannonEnt = calcShannonEnt(dataSet) # print(shannonEnt) # 将原数据集按索引和结果返回划分数据集,并将相应索引的源数据删除 def splitDataSet(dataset, axis, value): retdataset = [] for featvet in dataset: if featvet[axis] == value: reducedfeatvet = featvet[:axis] reducedfeatvet.extend(featvet[axis+1:]) retdataset.append(reducedfeatvet) # print(retdataset) return retdataset # splitDataSet(dataSet, 0, 1) # 获取当前最好的划分数据集的索引 def chooseBestfeatureTosplit(dataset): numfeatures = len(dataset[0]) - 1 baseEntropy = calcShannonEnt(dataset) bestinfogain = 0.0 bestfeature = -1 feature_list = [] for i in range(numfeatures): featlist = [example[i] for example in dataset] uniquevals = set(featlist) # 划分数据集的结果的集合 newEntropy = 0.0 # 计算每个类型的熵 for value in uniquevals: subdataset = splitDataSet(dataset, i, value) prob = len(subdataset)/float(len(dataset)) newEntropy += prob * calcShannonEnt(subdataset) # print(subdataset, uniquevals, value, i) infogain = baseEntropy - newEntropy if infogain > bestinfogain: bestinfogain = infogain bestfeature = i # 获取熵最小的类型,作为此级划分的首选项,因为可以找出类型最纯的一个划分数据集 # print(i, newEntropy, uniquevals) feature_list.append(newEntropy) # print(bestfeature) # print(feature_list) return bestfeature # chooseBestfeatureTosplit(dataSet) import operator # 划分数据集的结果的个数排序 def majorityCnt(classList): classCount = {} for vote in classList: if vote not in classCount.keys(): classCount[vote] = 0 classCount[vote] += 1 sortedclasscount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True) return sortedclasscount[0][0] classcount = majorityCnt(labels) # 创建决策树 def createtree(dataset, labels): classlist = [example[-1] for example in dataset] if classlist.count(classlist[0]) == len(classlist): return classlist[0] if len(dataset[0]) == 1: return majorityCnt(classlist) bestFeat = chooseBestfeatureTosplit(dataset) # 得到最好的划分数据集的索引 bestfeatlabel = labels[bestFeat] # 根据索引取得相应的特征 mytree = {bestfeatlabel:{}} # 按特征构建决策树 del labels[bestFeat] # 清理已使用的特征 featvals = [example[bestFeat] for example in dataset] # 根据索引取得相应的数据集的值 uniquevals = set(featvals) # 列表转为集合,实现数据去重 for vals in uniquevals: sublabels = labels[:] mytree[bestfeatlabel][vals] = createtree(splitDataSet(dataset, bestFeat, vals), sublabels) return mytree mytree = createtree(dataSet, labels) print(mytree)
# 得到的输出结果
# {'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}