在学习机器学习的源码,记录下对该代码的理解。
1.1 计算数据集的熵
待分析的数据集列表如下,首先根据表3-1构建数据集。
源码如下:
def createDataSet():
dataSet = [[1, 1, 'yes'], #index=0,就是“不浮出水面是否可以生存”,index=1,就是“是否有脚蹼”,最后一个是"是否鱼类"
[1, 1, 'yes'],
[1, 0, 'no'],
[0, 1, 'no'],
[0, 1, 'no']]
labels = ['no surfacing','flippers']
#change to discrete values
return dataSet, labels
这个比较简单。
计算该数据集熵的源码:
def calcShannonEnt(dataSet):
numEntries = len(dataSet)#该数据集长度,即有多少个数据,这里等于5
labelCounts = {}#特征结果字典,比如以index=2,对数据集进行分类,则labelCounts就是yes和no的个数统计,YES:2, no:3;这样的字典。
for featVec in dataSet: #the the number of unique elements and their occurance,逐行遍历
currentLabel = featVec[-1]#取每个元素的倒数第一个值,也就是index=2这一列
if currentLabel not in labelCounts.keys(): labelCounts[currentLabel] = 0#如果字典中,不包含当前特征值,则字典中加入该特征。
labelCounts[currentLabel] += 1#该特征计数
shannonEnt = 0.0
for key in labelCounts:
prob = float(labelCounts[key])/numEntries#分别计算YES和NO 出现的概率,这里分别为0.6,和0.4
shannonEnt -= prob * log(prob,2) #log base 2#根据计数公式,计算熵
return shannonEnt
1.2 根据特征,划分数据集
dataSet就是数据集,axis就是选择的特征,value,就是对应特征的值。
这个方法比较简单,以上面的dataSet为例,如果axis=0,就是选择第一列特征,即:"不浮出水面是否可以生存";这个特征有两个值,就是1和0,所以对应的value就是1和0;
def splitDataSet(dataSet, axis, value):
retDataSet = []
for featVec in dataSet:
if featVec[axis] == value:
reducedFeatVec = featVec[:axis] #chop out axis used for splitting
reducedFeatVec.extend(featVec[axis+1:])
retDataSet.append(reducedFeatVec)
return retDataSet
如果分别执行执行以下代码:
subDataSet1 = splitDataSet(dataSet, 0, 1)
subDataSet2= splitDataSet(dataSet, 0, 0)
分别得到结果是:
subDataSet1 = subDataSet2 =
可见,这个方法就是根据特征值的不同把数据集划分子数据集。在这里,就是根据特征“不浮出水面是否可以生存”的值1和0,分成了两个上图两个子集。
1.3 选择最佳分类特征
当我们拿到一个数据集,需要用决策树进行分类是,我们如何从众多特征中选择一个特征,作为决策树的第一个分类点呢?比如上述“不浮出水面是否可以生存” 和“是否有脚蹼”这两个特征,我们应该先选哪个特征进行分类呢?
这就需要用到1.1和1.2的分类方法。
def chooseBestFeatureToSplit(dataSet):
numFeatures = len(dataSet[0]) - 1 #the last column is used for the labels 计算特征个数,这个例子中等于2
baseEntropy = calcShannonEnt(dataSet)#计算原始数据集的熵
bestInfoGain = 0.0; bestFeature = -1
for i in range(numFeatures): #iterate over all the features,根据特征进行迭代,从index=0,到1;
#获取index=0的特征对应的所有值,即取dataSet矩阵每列的所有值。
featList = [example[i] for example in dataSet]#create a list of all the examples of this feature,
uniqueVals = set(featList) #get a set of unique values,统计每个特征对应的值有多少可能,比如这里index=0的特征,只有两个值0,1
newEntropy = 0.0
for value in uniqueVals:
subDataSet = splitDataSet(dataSet, i, value)#针对每个特征,划分子集
#比如对index=0的特征进行子集划分,特征值为0的有2个,为1的有3个;权重分别为2/5, 3/5
prob = len(subDataSet)/float(len(dataSet))#计算每个子集的权重,
newEntropy += prob * calcShannonEnt(subDataSet)#计算总熵值,权重*子熵
infoGain = baseEntropy - newEntropy #calculate the info gain; ie reduction in entropy,计算分类后熵和原来熵的差值
if (infoGain > bestInfoGain): #compare this to the best gain so far,差值越大,(也就是子集熵越小),分类越合理。
bestInfoGain = infoGain #if better than current best, set to best
bestFeature = i
return bestFeature #returns an integer
综合来说,就是根据特征值,进行分类,计算每个分类之后熵值的大小,然后进行排序,熵值越小,分类越合理。
就优先以该特征值进行划分。