算法原理:
通过对特征值进行分类,使在对数据进行分类时一层一层的根据不同的特征值进行筛选,最终给出分类结果。
例:使用成绩作为特征值对成绩进行分类。
例:使用多个特征对学生进行分类。

信息增益
不论一个数据集有多少特征,每次划分数据集时只能选一个特征,那么第一次选择哪个特征作为划分的参考特征呢?答案一定是分类能力最好的那个特征。
如何判断哪一个特征分类能力最好呢?这时就要引入一个新的概念——信息增益。
划分数据集的大原则:将无序的数据变得更加有序。
在划分数据集之前或之后信息发生的变化称之为信息增益。计算每个特征值划分数据集获得的数据增益,获得信息增益最高的特征就是最好的选择。
数学基础
代码实现:
_# 计算数据的熵<br />_def calcShannonEnt(dataSet):<br /> numEntries = len(dataSet) _#数据量<br /> _labelCounts = {} _#统计类别和出现的次数。{类别 : 频次}<br /> _for featVec in dataSet:<br /> currentLabel = featVec[-1] _#获取每条数据最后一个元素,即类别<br /> #将类别添加到字典中,没有该类则以该类作为键,并赋值1;若该类已存在则将值加1<br /> _if currentLabel not in labelCounts.keys():<br /> labelCounts[currentLabel] = 0<br /> labelCounts[currentLabel] += 1<br /> shannonEnt = 0.0 _#初始化香农熵<br /> _for key in labelCounts:<br /> prob = float(labelCounts[key]) / numEntries _#P(xi)<br /> _shannonEnt -= prob * log(prob,2) _#熵<br /> _return shannonEnt
划分数据集
对划分数据集的熵进行度量,以便判断当前是否正确划分了数据集。
数据集:
def createDataSet():<br /> dataSet = [<br /> [1,1,"yes"],<br /> [1,1,"yes"],<br /> [1,0,"no"],<br /> [0,1,"no"],<br /> [0,1,"no"]<br /> ]<br /> labels = ["no surfacing","flippers"]<br /> return dataSet,labels
按照给定特征划分数据集:
_# 按照给定特征划分数据集<br />_def splitDataSet(dataSet,axis,value): _#待划分的数据集、划分数据集的特征(在一条数据中的索引)、需要返回的特征的值(特征要取的值)<br /> _retDataSet = []<br /> for featVec in dataSet: _#读取一行数据<br /> _if featVec[axis] == value: _# 若该条数据的指定特征的取值 = 要求的取值<br /> _reducedFeatVec = featVec[:axis] _# 将每一条数据的指定特征值前的元素赋给reducedFeatVec<br /> _reducedFeatVec.extend(featVec[axis+1:]) _# 将每条数据的指定特征值之后的所有元素分别添加到retDataSet<br /> _retDataSet.append(reducedFeatVec) _#将每一条数据的指定特征值前的元素以一个元素的形式添加到retDataSet<br /> _return retDataSet
调用及结果:
retDataSet = splitDataSet(dataSet,0,0) _#以第0个特征值取0划分数据集_
选择最好的数据集划分方式
遍历整个数据集,循环计算香农熵和调用splitDataSet()函数,找到最好的特征划分方式。_# 选择最好的数据集划分方式<br />_def chooseBestFeatureToSplit(dataSet):<br /> numFeatures = len(dataSet[0]) - 1 _#一条数据除label外的元素个数(特征数量)<br /> _baseEntropy = calcShannonEnt(dataSet) _#计算数据集的熵(初始数据集的熵)<br /> _bestInfoGain = 0.0<br /> bestFeature = -1 _#初始化最好的数据集划分特征<br /> _for i in range(numFeatures): _#取出每一个特征所在列的索引<br /> _featList = [example[i] for example in dataSet] _#将数据集中的某一种特征的值提取到列表featList<br /> _uniqueVals = set(featList) _#列表 --> 集合,即只统计出现了哪些特征值,而不管这些特征值出现了几次<br /> _newEntropy = 0.0 _#初始化熵<br /> _for value in uniqueVals: _#value为某特征的一个取值<br /> _subDataSet = splitDataSet(dataSet,i,value) _#按照指定特征的指定取值划分数据集<br /> _prob = len(subDataSet) / float(len(dataSet)) _#计算此时划分出的数据集的数据数量在全部数据中的占比<br /> # 划分出的数据集的熵<br /> _newEntropy += prob * calcShannonEnt(subDataSet) _#更新熵<br /> _infoGain = baseEntropy - newEntropy _#熵值越大,数据的离散程度越大<br /> _if(infoGain > bestInfoGain): _#对最好的数据集划分特征进行更新<br /> _bestInfoGain = infoGain<br /> bestFeature = i<br /> return bestFeature _#返回最好的数据集划分特征的索引_
调用及结果:
chooseBestFeatureToSplit(dataSet)
代码中的函数介绍:
列表表达式:
[表达式 for 迭代变量 in 可迭代对象 if 条件表达式]
—-等价于—->
for 迭代变量 in 可迭代对象:
if 条件表达式:
表达式
set(要转换为集合的对象):将其他对象(列表、元组、…)转换为集合类型
集合中的值不可重复,通常使用该方法获取列表或元组中出现的元素。
递归构建决策树
在前几个步骤中,构建了从数据集构造决策树需要的子模块,工作原理为:得到原始数据集,然后基于最好的特征值划分数据集,由于特征值可能多于两个,因此可能存在大于两个分支的数据集划分。第一次划分后,数据将被向下传递到树分支的下一个节点,在这个节点上,可以再次划分数据。故可以采取递归的原则处理数据集。
递归的条件是:程序遍历完所有划分数据集的属性,或者每个分支下的所有实例都是相同的分类。
获取出现次数最多的特征:
_# 用于返回特征值出现次数最多的特征<br />_def majorityCnt(classList): _# classList:数据集中每条数据的特征构成的列表<br /> _classCount = {}<br /> _# 将特征和出现次数存入字典<br /> _for vote in classList:<br /> if vote not in classCount.keys():<br /> classCount[vote] = 0<br /> classCount[vote] += 1<br /> _# 对字典按照值进行排序<br /> _sortedClassCount = sorted(classCount.items(),<br /> key=operator.itemgetter(1),reverse=True)<br /> return sortedClassCount[0][0] _#返回出现次数最多的特征_
创建树:
_#创建树<br />_def createTree(dataSet,labels):<br /> classList = [example[-1] for example in dataSet] _# 获取每条数据最后一个元素,即特征<br /> _if classList.count(classList[0]) == len(classList): _# 如果第一个特征的个数 = 整个特征列表的长度<br /> _return classList[0]<br /> if len(dataSet[0]) == 1:<br /> return majorityCnt(classList) _#返回出现次数最多的特征<br /> _bestFeat = chooseBestFeatureToSplit(dataSet) _#获取最好的数据集划分特征的索引<br /> _bestFeatLabel = labels[bestFeat] _#获取最好的数据集划分特征<br /> # 构建树<br /> _decisionTree = {bestFeatLabel:{}}<br /> del(labels[bestFeat]) _#将当前得到的最好的数据集划分特征从特征列表中去除<br /> _featValues = [example[bestFeat] for example in dataSet] _#获取每条数据中最好特征的取值<br /> _uniqueVals = set(featValues) _#提取最好特征取值列表中出现的值<br /> _for value in uniqueVals:<br /> subLabels = labels[:]<br /> decisionTree[bestFeatLabel][value] = createTree(<br /> splitDataSet(dataSet,bestFeat,value),subLabels _#按照最好特征的各个取值划分的数据集,去除了当前最好特征的特征列表<br /> _)<br /> return decisionTree
调用及结果:
createTree(dataSet,labels)

