from math import log
#计算给定数据集的香农熵
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 = float(labelCounts[key]) / numEntries
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 surfacint', 'flippers']
return dataSet, labels
#按照给定特征划分数据集
def splitDataSet(dataSet, axis, value):
retDataSet = []
for featVec in dataSet:
if featVec[axis] == value:
reducedFeatVec = featVec[:axis]
reducedFeatVec.extend(featVec[axis+1:])
retDataSet.append(reducedFeatVec)
return retDataSet
#选择最好的数据集划分方式
def chooseBestFeatureToSplit(dataSet):
numFeatures = len(dataSet[0]) - 1
baseEntropy = calcShannonEnt(dataSet)
bestInfoGain = 0.0
bestFeature = -1
#找出使得香农熵减少最多(最大信息增益)的特征--熵代表无序度
for i in range(numFeatures):
#第i个特征的所有的取值的列表
featList = [example[i] for example in dataSet]
#利用集合来得到没有重复元素的特征i的取值
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)
infoGain = baseEntropy - newEntropy
if(infoGain > bestInfoGain):
bestInfoGain = infoGain
bestFeature = i;
return bestFeature
>>> myDat
[[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]
>>> trees.chooseBestFeatureToSplit(myDat)
0
说明采取第一个特征可以最好地划分数据集。
事实上,采用第一个特征,划分出来的第一个集合有两个yes, 一个no, 第二个集合两个no
采用第二个特征,划分出来第一个集合是两个yes, 两个no, 第二个集合一个no
第一个特征比第二个特征划分得要集中一点,有序一点。
python代码的重要代码有注释可看。