机器学习实战--决策树算法
決策樹(shù)
決策樹(shù)(decision tree)是一種基本的分類與回歸方法。從判斷模塊引出的左右箭頭稱為分支,它可以達(dá)到另外一個(gè)判斷模塊或者終止模塊。分類決策樹(shù)模型是一種描述對(duì)實(shí)例進(jìn)行分類的樹(shù)形結(jié)構(gòu)。決策樹(shù)由節(jié)點(diǎn)(node)和有向邊(directed edge)組成。節(jié)點(diǎn)有內(nèi)部節(jié)點(diǎn)(internal node)和葉節(jié)點(diǎn)(leaf node)。內(nèi)部節(jié)點(diǎn)表示一個(gè)特征或者屬性,葉節(jié)點(diǎn)表示一個(gè)類。
1. 決策樹(shù)流程:
- 收集數(shù)據(jù):可以使用任何方法收集到的數(shù)據(jù)。
- 準(zhǔn)備數(shù)據(jù):收集完數(shù)據(jù)之后,我們要對(duì)數(shù)據(jù)進(jìn)行整理,將這些數(shù)據(jù)按照一定的要求進(jìn)行整理出來(lái),方便后續(xù)數(shù)據(jù)處理
- 分析數(shù)據(jù):可以使用任何方法,決策樹(shù)構(gòu)建完成之后,可以檢查我們的決策樹(shù)圖形是否符合預(yù)期要求。
- 訓(xùn)練算法:這個(gè)過(guò)程也就是構(gòu)建決策樹(shù),同樣也叫做決策樹(shù)學(xué)習(xí),就是構(gòu)建一個(gè)決策樹(shù)的數(shù)據(jù)結(jié)構(gòu)
- 測(cè)試算法:使用經(jīng)驗(yàn)樹(shù)計(jì)算錯(cuò)誤率,當(dāng)錯(cuò)誤率達(dá)到了可接受的范圍,這個(gè)決策樹(shù)就可以投放使用了。
- 使用算法:此步驟可以適用于任何監(jiān)督學(xué)習(xí)算法,而使用決策樹(shù)可以更好的理解數(shù)據(jù)的內(nèi)在含義。
決策樹(shù)一般構(gòu)建分成三個(gè)步驟:特征選擇、決策樹(shù)的生成、決策樹(shù)的剪枝
1.1 特征選擇
特征選擇在于選擇對(duì)訓(xùn)練數(shù)據(jù)具有分類的特征,這樣可以提高決策樹(shù)學(xué)習(xí)的效率,如果利用一個(gè)特征進(jìn)行分類的結(jié)果與隨機(jī)分類的結(jié)果沒(méi)有多大的區(qū)別,則稱這個(gè)特征是沒(méi)有分類能力的。經(jīng)驗(yàn)上扔掉這樣的特征對(duì)決策樹(shù)學(xué)習(xí)的精度影響不大。通常特征選擇的標(biāo)準(zhǔn)是信息增益(information gain)或者信息增益比,為了簡(jiǎn)單,本文使用信息增益作為選擇特征的標(biāo)準(zhǔn)。
希望通過(guò)所給的訓(xùn)練數(shù)據(jù)學(xué)習(xí)一個(gè)貸款申請(qǐng)的決策樹(shù),用于對(duì)未來(lái)的貸款申請(qǐng)進(jìn)行分類,即當(dāng)新的客戶提出貸款申請(qǐng)時(shí),根據(jù)申請(qǐng)人的特征利用決策樹(shù)是否批準(zhǔn)貸款申請(qǐng)。
特征選擇就是決定用哪個(gè)特征來(lái)劃分特征空間,比如上表兩個(gè)可能的決策樹(shù),分別由兩個(gè)不同特征的根節(jié)點(diǎn)構(gòu)成。年齡節(jié)點(diǎn)或者是否有工作兩個(gè)節(jié)點(diǎn)。不同的根節(jié)點(diǎn),對(duì)應(yīng)不同的決策樹(shù)。究竟哪個(gè)效果更好?就需要用到信息增益,可以很好的表示這一直觀的準(zhǔn)則。
什么是信息增益?在劃分?jǐn)?shù)據(jù)集之前之后發(fā)生的信息變化就是信息增益,知道如何計(jì)算信息增益,我們就可以計(jì)算每個(gè)特征值劃分?jǐn)?shù)據(jù)集獲得的信息增益,獲得信息增益最高的特征就是最好的選擇。
1.2 計(jì)算經(jīng)驗(yàn)熵
在進(jìn)行代碼編寫(xiě)之前,我們需要先對(duì)數(shù)據(jù)集進(jìn)行屬性標(biāo)注
- 年齡: 0代表青年,1代表中年,2代表老年;
- 有工作:0代表否,1代表是;
- 有自己的房子:0代表否,1代表是;
- 信貸情況:0代表一般,1代表好,2代表非常好;
- 類別(是否給貸款):no表示否,yes表示是。
計(jì)算經(jīng)驗(yàn)熵
代碼運(yùn)行結(jié)果如下圖所示,代碼先打印訓(xùn)練數(shù)據(jù)集,再打印經(jīng)驗(yàn)熵
1.3 計(jì)算信息增益
信息增益越大,特征對(duì)最終的分類結(jié)果的影響也就越大,我們選擇對(duì)最終分類結(jié)果影響最大的那個(gè)特征作為我們的分類特征
# -*- coding: UTF-8 -*- from math import log""" 函數(shù)說(shuō)明:計(jì)算給定數(shù)據(jù)集的經(jīng)驗(yàn)熵(香農(nóng)熵)Parameters:dataSet - 數(shù)據(jù)集 Returns:shannonEnt - 經(jīng)驗(yàn)熵(香農(nóng)熵) """ 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)沒(méi)有放入統(tǒng)計(jì)次數(shù)的字典,添加進(jìn)去labelCounts[currentLabel] = 0labelCounts[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ù)說(shuō)明:創(chuàng)建測(cè)試數(shù)據(jù)集Parameters:無(wú) Returns:dataSet - 數(shù)據(jù)集labels - 分類屬性 """ 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ù)說(shuō)明:按照給定特征劃分?jǐn)?shù)據(jù)集Parameters:dataSet - 待劃分的數(shù)據(jù)集axis - 劃分?jǐn)?shù)據(jù)集的特征value - 需要返回的特征的值 Returns:無(wú) """ 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ù)說(shuō)明:選擇最優(yōu)特征Parameters:dataSet - 數(shù)據(jù)集 Returns:bestFeature - 信息增益最大的(最優(yōu))特征的索引值 """ 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)))最優(yōu)特征索引值為2
1.4決策樹(shù)生成
1.4.1 ID3算法
D3算法的核心是在決策樹(shù)各個(gè)結(jié)點(diǎn)上對(duì)應(yīng)信息增益準(zhǔn)則選擇特征,遞歸地構(gòu)建決策樹(shù)。具體方法是:從根結(jié)點(diǎn)(root node)開(kāi)始,對(duì)結(jié)點(diǎn)計(jì)算所有可能的特征的信息增益,選擇信息增益最大的特征作為結(jié)點(diǎn)的特征,由該特征的不同取值建立子節(jié)點(diǎn);再對(duì)子結(jié)點(diǎn)遞歸地調(diào)用以上方法,構(gòu)建決策樹(shù);直到所有特征的信息增益均很小或沒(méi)有特征可以選擇為止,最后得到一個(gè)決策樹(shù)。ID3相當(dāng)于用極大似然法進(jìn)行概率模型的選擇。
1.4.2 構(gòu)建決策樹(shù)代碼
# -*- coding: UTF-8 -*- from math import log import operator""" 函數(shù)說(shuō)明:計(jì)算給定數(shù)據(jù)集的經(jīng)驗(yàn)熵(香農(nóng)熵)Parameters:dataSet - 數(shù)據(jù)集 Returns:shannonEnt - 經(jīng)驗(yàn)熵(香農(nóng)熵) """ 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)沒(méi)有放入統(tǒng)計(jì)次數(shù)的字典,添加進(jìn)去labelCounts[currentLabel] = 0labelCounts[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ù)說(shuō)明:創(chuàng)建測(cè)試數(shù)據(jù)集Parameters:無(wú) Returns:dataSet - 數(shù)據(jù)集labels - 特征標(biāo)簽 """ 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 = ['年齡', '有工作', '有自己的房子', '信貸情況'] #特征標(biāo)簽return dataSet, labels #返回?cái)?shù)據(jù)集和分類屬性""" 函數(shù)說(shuō)明:按照給定特征劃分?jǐn)?shù)據(jù)集Parameters:dataSet - 待劃分的數(shù)據(jù)集axis - 劃分?jǐn)?shù)據(jù)集的特征value - 需要返回的特征的值 Returns:無(wú) """ 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ù)說(shuō)明:選擇最優(yōu)特征Parameters:dataSet - 數(shù)據(jù)集 Returns:bestFeature - 信息增益最大的(最優(yōu))特征的索引值 """ 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 #返回信息增益最大的特征的索引值""" 函數(shù)說(shuō)明:統(tǒng)計(jì)classList中出現(xiàn)此處最多的元素(類標(biāo)簽)Parameters:classList - 類標(biāo)簽列表 Returns:sortedClassCount[0][0] - 出現(xiàn)此處最多的元素(類標(biāo)簽) """ def majorityCnt(classList):classCount = {}for vote in classList: #統(tǒng)計(jì)classList中每個(gè)元素出現(xiàn)的次數(shù)if vote not in classCount.keys():classCount[vote] = 0 classCount[vote] += 1sortedClassCount = sorted(classCount.items(), key = operator.itemgetter(1), reverse = True) #根據(jù)字典的值降序排序return sortedClassCount[0][0] #返回classList中出現(xiàn)次數(shù)最多的元素""" 函數(shù)說(shuō)明:創(chuàng)建決策樹(shù)Parameters:dataSet - 訓(xùn)練數(shù)據(jù)集labels - 分類屬性標(biāo)簽featLabels - 存儲(chǔ)選擇的最優(yōu)特征標(biāo)簽 Returns:myTree - 決策樹(shù) """ def createTree(dataSet, labels, featLabels):classList = [example[-1] for example in dataSet] #取分類標(biāo)簽(是否放貸:yes or no)if classList.count(classList[0]) == len(classList): #如果類別完全相同則停止繼續(xù)劃分return classList[0]if len(dataSet[0]) == 1: #遍歷完所有特征時(shí)返回出現(xiàn)次數(shù)最多的類標(biāo)簽return majorityCnt(classList)bestFeat = chooseBestFeatureToSplit(dataSet) #選擇最優(yōu)特征bestFeatLabel = labels[bestFeat] #最優(yōu)特征的標(biāo)簽featLabels.append(bestFeatLabel)myTree = {bestFeatLabel:{}} #根據(jù)最優(yōu)特征的標(biāo)簽生成樹(shù)del(labels[bestFeat]) #刪除已經(jīng)使用特征標(biāo)簽featValues = [example[bestFeat] for example in dataSet] #得到訓(xùn)練集中所有最優(yōu)特征的屬性值uniqueVals = set(featValues) #去掉重復(fù)的屬性值for value in uniqueVals: #遍歷特征,創(chuàng)建決策樹(shù)。 myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), labels, featLabels)return myTreeif __name__ == '__main__':dataSet, labels = createDataSet()featLabels = []myTree = createTree(dataSet, labels, featLabels)print(myTree)1.4.3決策樹(shù)可視化
# -*- coding: UTF-8 -*- from matplotlib.font_manager import FontProperties import matplotlib.pyplot as plt from math import log import operator""" 函數(shù)說(shuō)明:計(jì)算給定數(shù)據(jù)集的經(jīng)驗(yàn)熵(香農(nóng)熵)Parameters:dataSet - 數(shù)據(jù)集 Returns:shannonEnt - 經(jīng)驗(yàn)熵(香農(nóng)熵) """ 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)沒(méi)有放入統(tǒng)計(jì)次數(shù)的字典,添加進(jìn)去labelCounts[currentLabel] = 0labelCounts[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ù)說(shuō)明:創(chuàng)建測(cè)試數(shù)據(jù)集Parameters:無(wú) Returns:dataSet - 數(shù)據(jù)集labels - 特征標(biāo)簽 """ 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 = ['年齡', '有工作', '有自己的房子', '信貸情況'] #特征標(biāo)簽return dataSet, labels #返回?cái)?shù)據(jù)集和分類屬性""" 函數(shù)說(shuō)明:按照給定特征劃分?jǐn)?shù)據(jù)集Parameters:dataSet - 待劃分的數(shù)據(jù)集axis - 劃分?jǐn)?shù)據(jù)集的特征value - 需要返回的特征的值 Returns:無(wú) """ 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ù)說(shuō)明:選擇最優(yōu)特征Parameters:dataSet - 數(shù)據(jù)集 Returns:bestFeature - 信息增益最大的(最優(yōu))特征的索引值 """ 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 #返回信息增益最大的特征的索引值""" 函數(shù)說(shuō)明:統(tǒng)計(jì)classList中出現(xiàn)此處最多的元素(類標(biāo)簽)Parameters:classList - 類標(biāo)簽列表 Returns:sortedClassCount[0][0] - 出現(xiàn)此處最多的元素(類標(biāo)簽) """ def majorityCnt(classList):classCount = {}for vote in classList: #統(tǒng)計(jì)classList中每個(gè)元素出現(xiàn)的次數(shù)if vote not in classCount.keys():classCount[vote] = 0 classCount[vote] += 1sortedClassCount = sorted(classCount.items(), key = operator.itemgetter(1), reverse = True) #根據(jù)字典的值降序排序return sortedClassCount[0][0] #返回classList中出現(xiàn)次數(shù)最多的元素""" 函數(shù)說(shuō)明:創(chuàng)建決策樹(shù)Parameters:dataSet - 訓(xùn)練數(shù)據(jù)集labels - 分類屬性標(biāo)簽featLabels - 存儲(chǔ)選擇的最優(yōu)特征標(biāo)簽 Returns:myTree - 決策樹(shù) """ def createTree(dataSet, labels, featLabels):classList = [example[-1] for example in dataSet] #取分類標(biāo)簽(是否放貸:yes or no)if classList.count(classList[0]) == len(classList): #如果類別完全相同則停止繼續(xù)劃分return classList[0]if len(dataSet[0]) == 1: #遍歷完所有特征時(shí)返回出現(xiàn)次數(shù)最多的類標(biāo)簽return majorityCnt(classList)bestFeat = chooseBestFeatureToSplit(dataSet) #選擇最優(yōu)特征bestFeatLabel = labels[bestFeat] #最優(yōu)特征的標(biāo)簽featLabels.append(bestFeatLabel)myTree = {bestFeatLabel:{}} #根據(jù)最優(yōu)特征的標(biāo)簽生成樹(shù)del(labels[bestFeat]) #刪除已經(jīng)使用特征標(biāo)簽featValues = [example[bestFeat] for example in dataSet] #得到訓(xùn)練集中所有最優(yōu)特征的屬性值uniqueVals = set(featValues) #去掉重復(fù)的屬性值for value in uniqueVals: #遍歷特征,創(chuàng)建決策樹(shù)。 myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), labels, featLabels)return myTree""" 函數(shù)說(shuō)明:獲取決策樹(shù)葉子結(jié)點(diǎn)的數(shù)目Parameters:myTree - 決策樹(shù) Returns:numLeafs - 決策樹(shù)的葉子結(jié)點(diǎn)的數(shù)目 """ def getNumLeafs(myTree):numLeafs = 0 #初始化葉子firstStr = next(iter(myTree)) #python3中myTree.keys()返回的是dict_keys,不在是list,所以不能使用myTree.keys()[0]的方法獲取結(jié)點(diǎn)屬性,可以使用list(myTree.keys())[0]secondDict = myTree[firstStr] #獲取下一組字典for key in secondDict.keys():if type(secondDict[key]).__name__=='dict': #測(cè)試該結(jié)點(diǎn)是否為字典,如果不是字典,代表此結(jié)點(diǎn)為葉子結(jié)點(diǎn)numLeafs += getNumLeafs(secondDict[key])else: numLeafs +=1return numLeafs""" 函數(shù)說(shuō)明:獲取決策樹(shù)的層數(shù)Parameters:myTree - 決策樹(shù) Returns:maxDepth - 決策樹(shù)的層數(shù) """ def getTreeDepth(myTree):maxDepth = 0 #初始化決策樹(shù)深度firstStr = next(iter(myTree)) #python3中myTree.keys()返回的是dict_keys,不在是list,所以不能使用myTree.keys()[0]的方法獲取結(jié)點(diǎn)屬性,可以使用list(myTree.keys())[0]secondDict = myTree[firstStr] #獲取下一個(gè)字典for key in secondDict.keys():if type(secondDict[key]).__name__=='dict': #測(cè)試該結(jié)點(diǎn)是否為字典,如果不是字典,代表此結(jié)點(diǎn)為葉子結(jié)點(diǎn)thisDepth = 1 + getTreeDepth(secondDict[key])else: thisDepth = 1if thisDepth > maxDepth: maxDepth = thisDepth #更新層數(shù)return maxDepth""" 函數(shù)說(shuō)明:繪制結(jié)點(diǎn)Parameters:nodeTxt - 結(jié)點(diǎn)名centerPt - 文本位置parentPt - 標(biāo)注的箭頭位置nodeType - 結(jié)點(diǎn)格式 Returns:無(wú) """ def plotNode(nodeTxt, centerPt, parentPt, nodeType):arrow_args = dict(arrowstyle="<-") #定義箭頭格式font = FontProperties(fname=r"c:\windows\fonts\simsun.ttc", size=14) #設(shè)置中文字體createPlot.ax1.annotate(nodeTxt, xy=parentPt, xycoords='axes fraction', #繪制結(jié)點(diǎn)xytext=centerPt, textcoords='axes fraction',va="center", ha="center", bbox=nodeType, arrowprops=arrow_args, FontProperties=font)""" 函數(shù)說(shuō)明:標(biāo)注有向邊屬性值Parameters:cntrPt、parentPt - 用于計(jì)算標(biāo)注位置txtString - 標(biāo)注的內(nèi)容 Returns:無(wú) """ def plotMidText(cntrPt, parentPt, txtString):xMid = (parentPt[0]-cntrPt[0])/2.0 + cntrPt[0] #計(jì)算標(biāo)注位置 yMid = (parentPt[1]-cntrPt[1])/2.0 + cntrPt[1]createPlot.ax1.text(xMid, yMid, txtString, va="center", ha="center", rotation=30)""" 函數(shù)說(shuō)明:繪制決策樹(shù)Parameters:myTree - 決策樹(shù)(字典)parentPt - 標(biāo)注的內(nèi)容nodeTxt - 結(jié)點(diǎn)名 Returns:無(wú) """ def plotTree(myTree, parentPt, nodeTxt):decisionNode = dict(boxstyle="sawtooth", fc="0.8") #設(shè)置結(jié)點(diǎn)格式leafNode = dict(boxstyle="round4", fc="0.8") #設(shè)置葉結(jié)點(diǎn)格式numLeafs = getNumLeafs(myTree) #獲取決策樹(shù)葉結(jié)點(diǎn)數(shù)目,決定了樹(shù)的寬度depth = getTreeDepth(myTree) #獲取決策樹(shù)層數(shù)firstStr = next(iter(myTree)) #下個(gè)字典 cntrPt = (plotTree.xOff + (1.0 + float(numLeafs))/2.0/plotTree.totalW, plotTree.yOff) #中心位置plotMidText(cntrPt, parentPt, nodeTxt) #標(biāo)注有向邊屬性值plotNode(firstStr, cntrPt, parentPt, decisionNode) #繪制結(jié)點(diǎn)secondDict = myTree[firstStr] #下一個(gè)字典,也就是繼續(xù)繪制子結(jié)點(diǎn)plotTree.yOff = plotTree.yOff - 1.0/plotTree.totalD #y偏移for key in secondDict.keys(): if type(secondDict[key]).__name__=='dict': #測(cè)試該結(jié)點(diǎn)是否為字典,如果不是字典,代表此結(jié)點(diǎn)為葉子結(jié)點(diǎn)plotTree(secondDict[key],cntrPt,str(key)) #不是葉結(jié)點(diǎn),遞歸調(diào)用繼續(xù)繪制else: #如果是葉結(jié)點(diǎn),繪制葉結(jié)點(diǎn),并標(biāo)注有向邊屬性值 plotTree.xOff = plotTree.xOff + 1.0/plotTree.totalWplotNode(secondDict[key], (plotTree.xOff, plotTree.yOff), cntrPt, leafNode)plotMidText((plotTree.xOff, plotTree.yOff), cntrPt, str(key))plotTree.yOff = plotTree.yOff + 1.0/plotTree.totalD""" 函數(shù)說(shuō)明:創(chuàng)建繪制面板Parameters:inTree - 決策樹(shù)(字典) Returns:無(wú) """ def createPlot(inTree):fig = plt.figure(1, facecolor='white') #創(chuàng)建figfig.clf() #清空f(shuō)igaxprops = dict(xticks=[], yticks=[])createPlot.ax1 = plt.subplot(111, frameon=False, **axprops) #去掉x、y軸plotTree.totalW = float(getNumLeafs(inTree)) #獲取決策樹(shù)葉結(jié)點(diǎn)數(shù)目plotTree.totalD = float(getTreeDepth(inTree)) #獲取決策樹(shù)層數(shù)plotTree.xOff = -0.5/plotTree.totalW; plotTree.yOff = 1.0; #x偏移plotTree(inTree, (0.5,1.0), '') #繪制決策樹(shù)plt.show() #顯示繪制結(jié)果 if __name__ == '__main__':dataSet, labels = createDataSet()featLabels = []myTree = createTree(dataSet, labels, featLabels)print(myTree) createPlot(myTree)1.4.4 決策樹(shù)可視化
# -*- coding: UTF-8 -*- from matplotlib.font_manager import FontProperties import matplotlib.pyplot as plt from math import log import operator""" 函數(shù)說(shuō)明:計(jì)算給定數(shù)據(jù)集的經(jīng)驗(yàn)熵(香農(nóng)熵)Parameters:dataSet - 數(shù)據(jù)集 Returns:shannonEnt - 經(jīng)驗(yàn)熵(香農(nóng)熵)""" 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)沒(méi)有放入統(tǒng)計(jì)次數(shù)的字典,添加進(jìn)去labelCounts[currentLabel] = 0labelCounts[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ù)說(shuō)明:創(chuàng)建測(cè)試數(shù)據(jù)集Parameters:無(wú) Returns:dataSet - 數(shù)據(jù)集labels - 特征標(biāo)簽""" 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 = ['年齡', '有工作', '有自己的房子', '信貸情況'] #特征標(biāo)簽return dataSet, labels #返回?cái)?shù)據(jù)集和分類屬性""" 函數(shù)說(shuō)明:按照給定特征劃分?jǐn)?shù)據(jù)集Parameters:dataSet - 待劃分的數(shù)據(jù)集axis - 劃分?jǐn)?shù)據(jù)集的特征value - 需要返回的特征的值 Returns:無(wú)""" 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ù)說(shuō)明:選擇最優(yōu)特征Parameters:dataSet - 數(shù)據(jù)集 Returns:bestFeature - 信息增益最大的(最優(yōu))特征的索引值""" 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 #返回信息增益最大的特征的索引值""" 函數(shù)說(shuō)明:統(tǒng)計(jì)classList中出現(xiàn)此處最多的元素(類標(biāo)簽)Parameters:classList - 類標(biāo)簽列表 Returns:sortedClassCount[0][0] - 出現(xiàn)此處最多的元素(類標(biāo)簽)""" def majorityCnt(classList):classCount = {}for vote in classList: #統(tǒng)計(jì)classList中每個(gè)元素出現(xiàn)的次數(shù)if vote not in classCount.keys():classCount[vote] = 0 classCount[vote] += 1sortedClassCount = sorted(classCount.items(), key = operator.itemgetter(1), reverse = True) #根據(jù)字典的值降序排序return sortedClassCount[0][0] #返回classList中出現(xiàn)次數(shù)最多的元素""" 函數(shù)說(shuō)明:創(chuàng)建決策樹(shù)Parameters:dataSet - 訓(xùn)練數(shù)據(jù)集labels - 分類屬性標(biāo)簽featLabels - 存儲(chǔ)選擇的最優(yōu)特征標(biāo)簽 Returns:myTree - 決策樹(shù)""" def createTree(dataSet, labels, featLabels):classList = [example[-1] for example in dataSet] #取分類標(biāo)簽(是否放貸:yes or no)if classList.count(classList[0]) == len(classList): #如果類別完全相同則停止繼續(xù)劃分return classList[0]if len(dataSet[0]) == 1: #遍歷完所有特征時(shí)返回出現(xiàn)次數(shù)最多的類標(biāo)簽return majorityCnt(classList)bestFeat = chooseBestFeatureToSplit(dataSet) #選擇最優(yōu)特征bestFeatLabel = labels[bestFeat] #最優(yōu)特征的標(biāo)簽featLabels.append(bestFeatLabel)myTree = {bestFeatLabel:{}} #根據(jù)最優(yōu)特征的標(biāo)簽生成樹(shù)del(labels[bestFeat]) #刪除已經(jīng)使用特征標(biāo)簽featValues = [example[bestFeat] for example in dataSet] #得到訓(xùn)練集中所有最優(yōu)特征的屬性值uniqueVals = set(featValues) #去掉重復(fù)的屬性值for value in uniqueVals: #遍歷特征,創(chuàng)建決策樹(shù)。 myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), labels, featLabels)return myTree""" 函數(shù)說(shuō)明:獲取決策樹(shù)葉子結(jié)點(diǎn)的數(shù)目Parameters:myTree - 決策樹(shù) Returns:numLeafs - 決策樹(shù)的葉子結(jié)點(diǎn)的數(shù)目""" def getNumLeafs(myTree):numLeafs = 0 #初始化葉子firstStr = next(iter(myTree)) #python3中myTree.keys()返回的是dict_keys,不在是list,所以不能使用myTree.keys()[0]的方法獲取結(jié)點(diǎn)屬性,可以使用list(myTree.keys())[0]secondDict = myTree[firstStr] #獲取下一組字典for key in secondDict.keys():if type(secondDict[key]).__name__=='dict': #測(cè)試該結(jié)點(diǎn)是否為字典,如果不是字典,代表此結(jié)點(diǎn)為葉子結(jié)點(diǎn)numLeafs += getNumLeafs(secondDict[key])else: numLeafs +=1return numLeafs""" 函數(shù)說(shuō)明:獲取決策樹(shù)的層數(shù)Parameters:myTree - 決策樹(shù) Returns:maxDepth - 決策樹(shù)的層數(shù)""" def getTreeDepth(myTree):maxDepth = 0 #初始化決策樹(shù)深度firstStr = next(iter(myTree)) #python3中myTree.keys()返回的是dict_keys,不在是list,所以不能使用myTree.keys()[0]的方法獲取結(jié)點(diǎn)屬性,可以使用list(myTree.keys())[0]secondDict = myTree[firstStr] #獲取下一個(gè)字典for key in secondDict.keys():if type(secondDict[key]).__name__=='dict': #測(cè)試該結(jié)點(diǎn)是否為字典,如果不是字典,代表此結(jié)點(diǎn)為葉子結(jié)點(diǎn)thisDepth = 1 + getTreeDepth(secondDict[key])else: thisDepth = 1if thisDepth > maxDepth: maxDepth = thisDepth #更新層數(shù)return maxDepth""" 函數(shù)說(shuō)明:繪制結(jié)點(diǎn)Parameters:nodeTxt - 結(jié)點(diǎn)名centerPt - 文本位置parentPt - 標(biāo)注的箭頭位置nodeType - 結(jié)點(diǎn)格式 Returns:無(wú)""" def plotNode(nodeTxt, centerPt, parentPt, nodeType):arrow_args = dict(arrowstyle="<-") #定義箭頭格式font = FontProperties(fname=r"c:\windows\fonts\simsun.ttc", size=14) #設(shè)置中文字體createPlot.ax1.annotate(nodeTxt, xy=parentPt, xycoords='axes fraction', #繪制結(jié)點(diǎn)xytext=centerPt, textcoords='axes fraction',va="center", ha="center", bbox=nodeType, arrowprops=arrow_args, FontProperties=font)""" 函數(shù)說(shuō)明:標(biāo)注有向邊屬性值Parameters:cntrPt、parentPt - 用于計(jì)算標(biāo)注位置txtString - 標(biāo)注的內(nèi)容 Returns:無(wú)""" def plotMidText(cntrPt, parentPt, txtString):xMid = (parentPt[0]-cntrPt[0])/2.0 + cntrPt[0] #計(jì)算標(biāo)注位置 yMid = (parentPt[1]-cntrPt[1])/2.0 + cntrPt[1]createPlot.ax1.text(xMid, yMid, txtString, va="center", ha="center", rotation=30)""" 函數(shù)說(shuō)明:繪制決策樹(shù)Parameters:myTree - 決策樹(shù)(字典)parentPt - 標(biāo)注的內(nèi)容nodeTxt - 結(jié)點(diǎn)名 Returns:無(wú)""" def plotTree(myTree, parentPt, nodeTxt):decisionNode = dict(boxstyle="sawtooth", fc="0.8") #設(shè)置結(jié)點(diǎn)格式leafNode = dict(boxstyle="round4", fc="0.8") #設(shè)置葉結(jié)點(diǎn)格式numLeafs = getNumLeafs(myTree) #獲取決策樹(shù)葉結(jié)點(diǎn)數(shù)目,決定了樹(shù)的寬度depth = getTreeDepth(myTree) #獲取決策樹(shù)層數(shù)firstStr = next(iter(myTree)) #下個(gè)字典 cntrPt = (plotTree.xOff + (1.0 + float(numLeafs))/2.0/plotTree.totalW, plotTree.yOff) #中心位置plotMidText(cntrPt, parentPt, nodeTxt) #標(biāo)注有向邊屬性值plotNode(firstStr, cntrPt, parentPt, decisionNode) #繪制結(jié)點(diǎn)secondDict = myTree[firstStr] #下一個(gè)字典,也就是繼續(xù)繪制子結(jié)點(diǎn)plotTree.yOff = plotTree.yOff - 1.0/plotTree.totalD #y偏移for key in secondDict.keys(): if type(secondDict[key]).__name__=='dict': #測(cè)試該結(jié)點(diǎn)是否為字典,如果不是字典,代表此結(jié)點(diǎn)為葉子結(jié)點(diǎn)plotTree(secondDict[key],cntrPt,str(key)) #不是葉結(jié)點(diǎn),遞歸調(diào)用繼續(xù)繪制else: #如果是葉結(jié)點(diǎn),繪制葉結(jié)點(diǎn),并標(biāo)注有向邊屬性值 plotTree.xOff = plotTree.xOff + 1.0/plotTree.totalWplotNode(secondDict[key], (plotTree.xOff, plotTree.yOff), cntrPt, leafNode)plotMidText((plotTree.xOff, plotTree.yOff), cntrPt, str(key))plotTree.yOff = plotTree.yOff + 1.0/plotTree.totalD""" 函數(shù)說(shuō)明:創(chuàng)建繪制面板Parameters:inTree - 決策樹(shù)(字典) Returns:無(wú)""" def createPlot(inTree):fig = plt.figure(1, facecolor='white') #創(chuàng)建figfig.clf() #清空f(shuō)igaxprops = dict(xticks=[], yticks=[])createPlot.ax1 = plt.subplot(111, frameon=False, **axprops) #去掉x、y軸plotTree.totalW = float(getNumLeafs(inTree)) #獲取決策樹(shù)葉結(jié)點(diǎn)數(shù)目plotTree.totalD = float(getTreeDepth(inTree)) #獲取決策樹(shù)層數(shù)plotTree.xOff = -0.5/plotTree.totalW; plotTree.yOff = 1.0; #x偏移plotTree(inTree, (0.5,1.0), '') #繪制決策樹(shù)plt.show() #顯示繪制結(jié)果 if __name__ == '__main__':dataSet, labels = createDataSet()featLabels = []myTree = createTree(dataSet, labels, featLabels)print(myTree) createPlot(myTree)1.4.5決策樹(shù)執(zhí)行分類
# -*- coding: UTF-8 -*- from math import log import operator""" 函數(shù)說(shuō)明:計(jì)算給定數(shù)據(jù)集的經(jīng)驗(yàn)熵(香農(nóng)熵)Parameters:dataSet - 數(shù)據(jù)集 Returns:shannonEnt - 經(jīng)驗(yàn)熵(香農(nóng)熵)""" 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)沒(méi)有放入統(tǒng)計(jì)次數(shù)的字典,添加進(jìn)去labelCounts[currentLabel] = 0labelCounts[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ù)說(shuō)明:創(chuàng)建測(cè)試數(shù)據(jù)集Parameters:無(wú) Returns:dataSet - 數(shù)據(jù)集labels - 特征標(biāo)簽""" 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 = ['年齡', '有工作', '有自己的房子', '信貸情況'] #特征標(biāo)簽return dataSet, labels #返回?cái)?shù)據(jù)集和分類屬性""" 函數(shù)說(shuō)明:按照給定特征劃分?jǐn)?shù)據(jù)集Parameters:dataSet - 待劃分的數(shù)據(jù)集axis - 劃分?jǐn)?shù)據(jù)集的特征value - 需要返回的特征的值 Returns:無(wú)""" 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ù)說(shuō)明:選擇最優(yōu)特征Parameters:dataSet - 數(shù)據(jù)集 Returns:bestFeature - 信息增益最大的(最優(yōu))特征的索引值""" 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 #返回信息增益最大的特征的索引值""" 函數(shù)說(shuō)明:統(tǒng)計(jì)classList中出現(xiàn)此處最多的元素(類標(biāo)簽)Parameters:classList - 類標(biāo)簽列表 Returns:sortedClassCount[0][0] - 出現(xiàn)此處最多的元素(類標(biāo)簽)""" def majorityCnt(classList):classCount = {}for vote in classList: #統(tǒng)計(jì)classList中每個(gè)元素出現(xiàn)的次數(shù)if vote not in classCount.keys():classCount[vote] = 0 classCount[vote] += 1sortedClassCount = sorted(classCount.items(), key = operator.itemgetter(1), reverse = True) #根據(jù)字典的值降序排序return sortedClassCount[0][0] #返回classList中出現(xiàn)次數(shù)最多的元素""" 函數(shù)說(shuō)明:創(chuàng)建決策樹(shù)Parameters:dataSet - 訓(xùn)練數(shù)據(jù)集labels - 分類屬性標(biāo)簽featLabels - 存儲(chǔ)選擇的最優(yōu)特征標(biāo)簽 Returns:myTree - 決策樹(shù)""" def createTree(dataSet, labels, featLabels):classList = [example[-1] for example in dataSet] #取分類標(biāo)簽(是否放貸:yes or no)if classList.count(classList[0]) == len(classList): #如果類別完全相同則停止繼續(xù)劃分return classList[0]if len(dataSet[0]) == 1: #遍歷完所有特征時(shí)返回出現(xiàn)次數(shù)最多的類標(biāo)簽return majorityCnt(classList)bestFeat = chooseBestFeatureToSplit(dataSet) #選擇最優(yōu)特征bestFeatLabel = labels[bestFeat] #最優(yōu)特征的標(biāo)簽featLabels.append(bestFeatLabel)myTree = {bestFeatLabel:{}} #根據(jù)最優(yōu)特征的標(biāo)簽生成樹(shù)del(labels[bestFeat]) #刪除已經(jīng)使用特征標(biāo)簽featValues = [example[bestFeat] for example in dataSet] #得到訓(xùn)練集中所有最優(yōu)特征的屬性值uniqueVals = set(featValues) #去掉重復(fù)的屬性值for value in uniqueVals: #遍歷特征,創(chuàng)建決策樹(shù)。 myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), labels, featLabels)return myTree""" 函數(shù)說(shuō)明:使用決策樹(shù)分類Parameters:inputTree - 已經(jīng)生成的決策樹(shù)featLabels - 存儲(chǔ)選擇的最優(yōu)特征標(biāo)簽testVec - 測(cè)試數(shù)據(jù)列表,順序?qū)?yīng)最優(yōu)特征標(biāo)簽 Returns:classLabel - 分類結(jié)果""" def classify(inputTree, featLabels, testVec):firstStr = next(iter(inputTree)) #獲取決策樹(shù)結(jié)點(diǎn)secondDict = inputTree[firstStr] #下一個(gè)字典featIndex = featLabels.index(firstStr) for key in secondDict.keys():if testVec[featIndex] == key:if type(secondDict[key]).__name__ == 'dict':classLabel = classify(secondDict[key], featLabels, testVec)else: classLabel = secondDict[key]return classLabelif __name__ == '__main__':dataSet, labels = createDataSet()featLabels = []myTree = createTree(dataSet, labels, featLabels)testVec = [0,1] #測(cè)試數(shù)據(jù)result = classify(myTree, featLabels, testVec)if result == 'yes':print('放貸')if result == 'no':print('不放貸')2.Sklearn–使用決策樹(shù)預(yù)測(cè)隱形眼鏡類型
Label的類型是age(年齡)、prescript(癥狀)、astigmatic(是否散光)、tearRate(眼淚數(shù)量)、class(最終分類)
2.1 Sklearn決策樹(shù)代碼
# -*- coding: UTF-8 -*- from sklearn.preprocessing import LabelEncoder, OneHotEncoder from sklearn.externals.six import StringIO from sklearn import tree import pandas as pd import numpy as np import pydotplusif __name__ == '__main__':with open('lenses.txt', 'r') as fr: #加載文件lenses = [inst.strip().split('\t') for inst in fr.readlines()] #處理文件lenses_target = [] #提取每組數(shù)據(jù)的類別,保存在列表里for each in lenses:lenses_target.append(each[-1])print(lenses_target)lensesLabels = ['age', 'prescript', 'astigmatic', 'tearRate'] #特征標(biāo)簽 lenses_list = [] #保存lenses數(shù)據(jù)的臨時(shí)列表lenses_dict = {} #保存lenses數(shù)據(jù)的字典,用于生成pandasfor each_label in lensesLabels: #提取信息,生成字典for each in lenses:lenses_list.append(each[lensesLabels.index(each_label)])lenses_dict[each_label] = lenses_listlenses_list = []# print(lenses_dict) #打印字典信息lenses_pd = pd.DataFrame(lenses_dict) #生成pandas.DataFrame# print(lenses_pd) #打印pandas.DataFramele = LabelEncoder() #創(chuàng)建LabelEncoder()對(duì)象,用于序列化 for col in lenses_pd.columns: #序列化lenses_pd[col] = le.fit_transform(lenses_pd[col])# print(lenses_pd) #打印編碼信息clf = tree.DecisionTreeClassifier(max_depth = 4) #創(chuàng)建DecisionTreeClassifier()類clf = clf.fit(lenses_pd.values.tolist(), lenses_target) #使用數(shù)據(jù),構(gòu)建決策樹(shù)dot_data = StringIO()tree.export_graphviz(clf, out_file = dot_data, #繪制決策樹(shù)feature_names = lenses_pd.keys(),class_names = clf.classes_,filled=True, rounded=True,special_characters=True)graph = pydotplus.graph_from_dot_data(dot_data.getvalue())graph.write_pdf("tree.pdf") #保存繪制好的決策樹(shù),以PDF的形式存儲(chǔ)。3.總結(jié)
決策樹(shù)的一些優(yōu)點(diǎn):
- 易于理解和解釋,決策樹(shù)可以可視化。
- 幾乎不需要數(shù)據(jù)預(yù)處理。其他方法經(jīng)常需要數(shù)據(jù)標(biāo)準(zhǔn)化,創(chuàng)建虛擬變量和刪除缺失值。決策樹(shù)還不支持缺失值。
- 使用樹(shù)的花費(fèi)(例如預(yù)測(cè)數(shù)據(jù))是訓(xùn)練數(shù)據(jù)點(diǎn)(data points)數(shù)量的對(duì)數(shù)。
- 可以同時(shí)處理數(shù)值變量和分類變量。其他方法大都適用于分析一種變量的集合。
- 可以處理多值輸出變量問(wèn)題。
- 使用白盒模型。如果一個(gè)情況被觀察到,使用邏輯判斷容易表示這種規(guī)則。相反,如果是黑盒模型(例如人工神經(jīng)網(wǎng)絡(luò)),結(jié)果會(huì)非常難解釋。
- 即使對(duì)真實(shí)模型來(lái)說(shuō),假設(shè)無(wú)效的情況下,也可以較好的適用。
決策樹(shù)的一些缺點(diǎn):
- 決策樹(shù)學(xué)習(xí)可能創(chuàng)建一個(gè)過(guò)于復(fù)雜的樹(shù),并不能很好的預(yù)測(cè)數(shù)據(jù)。也就是過(guò)擬合。修剪機(jī)制(現(xiàn)在不支持),設(shè)置一個(gè)葉子節(jié)點(diǎn)需要的最小樣本數(shù)量,或者數(shù)的最大深度,可以避免過(guò)擬合。
- 決策樹(shù)可能是不穩(wěn)定的,因?yàn)榧词狗浅P〉淖儺?#xff0c;可能會(huì)產(chǎn)生一顆完全不同的樹(shù)。這個(gè)問(wèn)題通過(guò)decision trees with an ensemble來(lái)緩解。
- 學(xué)習(xí)一顆最優(yōu)的決策樹(shù)是一個(gè)NP-完全問(wèn)題under several aspects of optimality and even for simple concepts。因此,傳統(tǒng)決策樹(shù)算法基于啟發(fā)式算法,例如貪婪算法,即每個(gè)節(jié)點(diǎn)創(chuàng)建最優(yōu)決策。這些算法不能產(chǎn)生一個(gè)全家最優(yōu)的決策樹(shù)。對(duì)樣本和特征隨機(jī)抽樣可以降低整體效果偏差。
- 概念難以學(xué)習(xí),因?yàn)闆Q策樹(shù)沒(méi)有很好的解釋他們,例如,XOR, parity or multiplexer problems.
- 如果某些分類占優(yōu)勢(shì),決策樹(shù)將會(huì)創(chuàng)建一棵有偏差的樹(shù)。因此,建議在訓(xùn)練之前,先抽樣使樣本均衡。
總結(jié)
以上是生活随笔為你收集整理的机器学习实战--决策树算法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Python游戏开发--外星人入侵(源代
- 下一篇: 统计学习/机器学习常用小知识