決策樹
計(jì)算經(jīng)驗(yàn)熵和信息增益
- 計(jì)算經(jīng)驗(yàn)熵
from math import log
'''
這是決策樹 特征選擇中的計(jì)算經(jīng)驗(yàn)熵 這一部分
'''
"""
函數(shù)說明: 創(chuàng)建數(shù)據(jù)集
Parameters:
無
Returns:
dataSet - 數(shù)據(jù)集
labels - 分類屬性
Author:
xiao zi
Modify:
2018年3月7日10:16:17
"""
def createDataSet():
dataSet = [[0,0,0,0,'no'],
[0,0,0,1,'no'],
[0,1,0,1,'yes'],
[0,1,1,0,'yes'],
[0,0,0,0,'no'],
[1,0,0,0,'no'],
[1,0,0,1,'no'],
[1,1,1,1,'yes'],
[1,0,1,2,'yes'],
[1,0,1,2,'yes'],
[2,0,1,2,'yes'],
[2,0,1,1,'yes'],
[2,1,0,1,'yes'],
[2,1,0,2,'yes'],
[2,0,0,0,'no']]
labels =['年齡','有工作','有自己的房子','信貸情況']
return dataSet,labels
"""
函數(shù)說明:計(jì)算給定數(shù)據(jù)集的經(jīng)驗(yàn)熵(香農(nóng)熵)
Parameters:
dataSet - 數(shù)據(jù)集
Returns:
shannonEnt - 經(jīng)驗(yàn)熵(香農(nóng)熵)
Author:
Jack Cui
Modify:
2017-03-29
"""
def calcShannonEnt(dataSet):
numEntires = len(dataSet) #返回?cái)?shù)據(jù)集的行數(shù)
labelCounts = {} #保存每個(gè)標(biāo)簽(Label)出現(xiàn)次數(shù)的字典
for featVec in dataSet: #對(duì)每組特征向量進(jìn)行統(tǒng)計(jì)
currentLabel = featVec[-1] #提取標(biāo)簽(Label)信息
if currentLabel not in labelCounts.keys(): #如果標(biāo)簽(Label)沒有放入統(tǒng)計(jì)次數(shù)的字典,添加進(jìn)去
labelCounts[currentLabel] = 0
labelCounts[currentLabel] += 1 #Label計(jì)數(shù)
shannonEnt = 0.0 #經(jīng)驗(yàn)熵(香農(nóng)熵)
for key in labelCounts: #計(jì)算香農(nóng)熵
prob = float(labelCounts[key]) / numEntires #選擇該標(biāo)簽(Label)的概率
shannonEnt -= prob * log(prob, 2) #利用公式計(jì)算
return shannonEnt #返回經(jīng)驗(yàn)熵(香農(nóng)熵)
if __name__ == '__main__':
dataSet, features = createDataSet()
print(dataSet)
print(calcShannonEnt(dataSet))
輸出:
[[0, 0, 0, 0, 'no'], [0, 0, 0, 1, 'no'], [0, 1, 0, 1, 'yes'], [0, 1, 1, 0, 'yes'], [0, 0, 0, 0, 'no'], [1, 0, 0, 0, 'no'], [1, 0, 0, 1, 'no'], [1, 1, 1, 1, 'yes'], [1, 0, 1, 2, 'yes'], [1, 0, 1, 2, 'yes'], [2, 0, 1, 2, 'yes'], [2, 0, 1, 1, 'yes'], [2, 1, 0, 1, 'yes'], [2, 1, 0, 2, 'yes'], [2, 0, 0, 0, 'no']]
0.9709505944546686
- 計(jì)算信息增益
# -*- coding: UTF-8 -*-
from math import log
"""
函數(shù)說明:計(jì)算給定數(shù)據(jù)集的經(jīng)驗(yàn)熵(香農(nóng)熵)
Parameters:
dataSet - 數(shù)據(jù)集
Returns:
shannonEnt - 經(jīng)驗(yàn)熵(香農(nóng)熵)
Author:
Jack Cui
Modify:
2017-03-29
"""
def calcShannonEnt(dataSet):
numEntires = len(dataSet) #返回?cái)?shù)據(jù)集的行數(shù)
labelCounts = {} #保存每個(gè)標(biāo)簽(Label)出現(xiàn)次數(shù)的字典
for featVec in dataSet: #對(duì)每組特征向量進(jìn)行統(tǒng)計(jì)
currentLabel = featVec[-1] #提取標(biāo)簽(Label)信息
if currentLabel not in labelCounts.keys(): #如果標(biāo)簽(Label)沒有放入統(tǒng)計(jì)次數(shù)的字典,添加進(jìn)去
labelCounts[currentLabel] = 0
labelCounts[currentLabel] += 1 #Label計(jì)數(shù)
shannonEnt = 0.0 #經(jīng)驗(yàn)熵(香農(nóng)熵)
for key in labelCounts: #計(jì)算香農(nóng)熵
prob = float(labelCounts[key]) / numEntires #選擇該標(biāo)簽(Label)的概率
shannonEnt -= prob * log(prob, 2) #利用公式計(jì)算
return shannonEnt #返回經(jīng)驗(yàn)熵(香農(nóng)熵)
"""
函數(shù)說明:創(chuàng)建測(cè)試數(shù)據(jù)集
Parameters:
無
Returns:
dataSet - 數(shù)據(jù)集
labels - 分類屬性
Author:
Jack Cui
Modify:
2017-07-20
"""
def createDataSet():
dataSet = [[0, 0, 0, 0, 'no'], #數(shù)據(jù)集
[0, 0, 0, 1, 'no'],
[0, 1, 0, 1, 'yes'],
[0, 1, 1, 0, 'yes'],
[0, 0, 0, 0, 'no'],
[1, 0, 0, 0, 'no'],
[1, 0, 0, 1, 'no'],
[1, 1, 1, 1, 'yes'],
[1, 0, 1, 2, 'yes'],
[1, 0, 1, 2, 'yes'],
[2, 0, 1, 2, 'yes'],
[2, 0, 1, 1, 'yes'],
[2, 1, 0, 1, 'yes'],
[2, 1, 0, 2, 'yes'],
[2, 0, 0, 0, 'no']]
labels = ['年齡', '有工作', '有自己的房子', '信貸情況'] #分類屬性
return dataSet, labels #返回?cái)?shù)據(jù)集和分類屬性
"""
函數(shù)說明:按照給定特征劃分?jǐn)?shù)據(jù)集
Parameters:
dataSet - 待劃分的數(shù)據(jù)集
axis - 劃分?jǐn)?shù)據(jù)集的特征
value - 需要返回的特征的值
Returns:
無
Author:
Jack Cui
Modify:
2017-03-30
"""
def splitDataSet(dataSet, axis, value):
retDataSet = [] #創(chuàng)建返回的數(shù)據(jù)集列表
for featVec in dataSet: #遍歷數(shù)據(jù)集
if featVec[axis] == value:
reducedFeatVec = featVec[:axis] #去掉axis特征
reducedFeatVec.extend(featVec[axis+1:]) #將符合條件的添加到返回的數(shù)據(jù)集
retDataSet.append(reducedFeatVec)
return retDataSet #返回劃分后的數(shù)據(jù)集
"""
函數(shù)說明:選擇最優(yōu)特征
Parameters:
dataSet - 數(shù)據(jù)集
Returns:
bestFeature - 信息增益最大的(最優(yōu))特征的索引值
Author:
Jack Cui
Modify:
2017-03-30
"""
def chooseBestFeatureToSplit(dataSet):
numFeatures = len(dataSet[0]) - 1 #特征數(shù)量
baseEntropy = calcShannonEnt(dataSet) #計(jì)算數(shù)據(jù)集的香農(nóng)熵
bestInfoGain = 0.0 #信息增益
bestFeature = -1 #最優(yōu)特征的索引值
for i in range(numFeatures): #遍歷所有特征
#獲取dataSet的第i個(gè)所有特征
featList = [example[i] for example in dataSet]
uniqueVals = set(featList) #創(chuàng)建set集合{},元素不可重復(fù)
newEntropy = 0.0 #經(jīng)驗(yàn)條件熵
for value in uniqueVals: #計(jì)算信息增益
subDataSet = splitDataSet(dataSet, i, value) #subDataSet劃分后的子集
prob = len(subDataSet) / float(len(dataSet)) #計(jì)算子集的概率
newEntropy += prob * calcShannonEnt(subDataSet) #根據(jù)公式計(jì)算經(jīng)驗(yàn)條件熵
infoGain = baseEntropy - newEntropy #信息增益
print("第%d個(gè)特征的增益為%.3f" % (i, infoGain)) #打印每個(gè)特征的信息增益
if (infoGain > bestInfoGain): #計(jì)算信息增益
bestInfoGain = infoGain #更新信息增益,找到最大的信息增益
bestFeature = i #記錄信息增益最大的特征的索引值
return bestFeature #返回信息增益最大的特征的索引值
if __name__ == '__main__':
dataSet, features = createDataSet()
print("最優(yōu)特征索引值:" + str(chooseBestFeatureToSplit(dataSet)))
注:
splitDataSet函數(shù)是用來選擇各個(gè)特征的子集的,比如選擇年齡(第0個(gè)特征)的青年(用0代表)的自己,我們可以調(diào)用splitDataSet(dataSet,0,0)這樣返回的子集就是年齡為青年的5個(gè)數(shù)據(jù)集。
輸出:
第0個(gè)特征的增益為0.083
第1個(gè)特征的增益為0.324
第2個(gè)特征的增益為0.420
第3個(gè)特征的增益為0.363
最優(yōu)特征索引值:2
總結(jié);
我們已經(jīng)學(xué)習(xí)了從數(shù)據(jù)集構(gòu)造決策樹算法所需要的子功能模塊,包括經(jīng)驗(yàn)熵的計(jì)算和最優(yōu)特征的選擇,其工作原理如下:得到原始數(shù)據(jù)集,然后基于最好的屬性值劃分?jǐn)?shù)據(jù)集,由于特征值可能多于兩個(gè),因此可能存在大于兩個(gè)分支的數(shù)據(jù)集劃分。第一次劃分之后,數(shù)據(jù)集被向下傳遞到樹的分支的下一個(gè)結(jié)點(diǎn)。在這個(gè)結(jié)點(diǎn)上,我們可以再次劃分?jǐn)?shù)據(jù)。因此我們可以采用遞歸的原則處理數(shù)據(jù)集。