決策樹 (二)
阿新 • • 發佈:2018-12-06
# -*- coding: utf-8 -*- """ 熵定義為資訊的期望值。 熵:表示隨機變數的不確定性。 條件熵:在一個條件下,隨機變數的不確定性。 資訊增益:熵 - 條件熵 在一個條件下,資訊不確定性減少的程度! 如果選擇一個特徵後,資訊增益最大(資訊不確定性減少的程度最大),那麼我們就選取這個特徵。 """ from math import log import operator """ 函式說明:建立測試集 Parameter: 無 Returns: dataSet 資料集 Labels 分類屬性 """ 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 = ['不放貸', '放貸'] Labels = ['年齡', '有工作', '有自己的房子', '信貸情況'] return dataSet, Labels """ 函式說明:計算給定資料集的經驗熵(夏農熵) Parameters: dataSet 資料集 Returns: shannonEnt 經驗熵 """ def calcShannonEnt(dataSet): #返回資料集的行數 numEntirs = 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]) / numEntirs # shannonEnt -= prob * log(prob, 2) return shannonEnt """ 函式說明:按照給定特徵劃分資料集 Parameters: dataSet 待劃分的資料集 axis 劃分資料集的特徵 value 需要返回的特徵值 Returns: retDataSet 返回的資料集列表 """ def splitDataSet(dataSet, axis, value): #返回的資料集列表 retDataSet = [] for featVec in dataSet: if featVec[axis] == value: #去掉axis特徵 reducedFeatVec = featVec[:axis] #reducedFeatVec = [] #將符合條件的新增到返回的資料集 reducedFeatVec.extend(featVec[axis+1 : ]) retDataSet.append(reducedFeatVec) return retDataSet """ 函式說明:選擇最優特徵 Paramaters: dataSet Returns: beatFeature 資訊增益最優的特徵的索引值 """ def chooseBestFeatureToSplit(dataSet): #特徵數量 numFeatures = len(dataSet[0]) - 1 #計算資料集的夏農熵 baseEntropy = calcShannonEnt(dataSet) #資訊增益 bestInfoGain = 0.0 #最優特徵的索引值 bestFeature = -1 for i in range(numFeatures): #獲取dataSet的第i個所有特徵 #將dataSet中的資料先按行依次放入example中, #然後取得example中的example[i]元素,放入列表featList中 #相當於取所有行的第一個值 #之所以這樣取,是因為dataSet是個列表,而不是矩陣,矩陣取第一列有方法 featList = [ example[i] for example in dataSet] #建立集合set,元素不可重複 uniqueVals = set(featList) #經驗條件熵 newEntropy = 0.0 #計算資訊增益 for value in uniqueVals: #subDataSet是劃分後的子集 subDataSet = splitDataSet(dataSet, i, value) #計運算元集的概率 prob = len(subDataSet) / float(len(dataSet)) #計算經驗條件熵 newEntropy += prob * calcShannonEnt(subDataSet) #資訊增益 infoGain = baseEntropy - newEntropy #列印每個特徵的資訊增益 #print("第%d個特徵的增益為:%.3f" % (i, infoGain)) if (infoGain > bestInfoGain): bestInfoGain = infoGain bestFeature = i return bestFeature """ 函式說明:統計classList中出現次數最多的元素(類標籤) Parameters: classList 類標籤列表 Returns: sortedClassCount[0][0] 出現次數最多的元素(類標籤) """ def majorityCnt(classList): classCount = {} #統計classList中每個元素出現的次數 for vote in classList: if vote not in classCount.keys(): classCount[vote] = 0 classCount[vote] += 1 #根據字典的值降序排序 sortedClassCount = sorted(classCount.items(), key = operator.itemgetter(1), reverse = True) return sortedClassCount[0][0] """ 函式說明:建立決策樹 Parameters: dataSet 訓練集 Labels 分類標籤 featLabels 儲存選擇的最優特徵標籤 Returns: myTree 決策樹 """ def createTree(dataSet, Labels, featLabels): #取dataSet每行的最後一列的元素構成新的列表 #相當於取dataSet最後一列的值 classList = [example[-1] for example in dataSet] #若類別完全相同就停止劃分 if classList.count(classList[0]) == len(classList): return classList[0] #遍歷完所有特徵值返回出現次數最多的標籤 if len(dataSet[0]) == 1 or len(Labels) == 0: return majorityCnt(classList) #選擇最優特徵值的索引 bestFeat = chooseBestFeatureToSplit(dataSet) bestFeatLabel = Labels[bestFeat] featLabels.append(bestFeatLabel) #生成決策樹 myTree = {bestFeatLabel:{}} #刪除已經使用的特徵標籤 del(Labels[bestFeat]) #得到訓練集中所有最優特徵值的屬性值 featValues = [example[bestFeat] for example in dataSet] #去除重複的屬性值 uniqueVals = set(featValues) #遍歷特徵,建立決策樹 for value in uniqueVals: myTree[bestFeatLabel][value] = createTree( splitDataSet(dataSet, bestFeat, value), Labels, featLabels) return myTree """ 函式說明:使用決策樹分類 Parameters: imputTree 已經生成的決策樹 featLabels 儲存選擇的最優特徵標籤 testVec 測試集 順序對應最優特徵標籤 Returns: classLabel 分類結果 """ def classify(inputTree, featLabels, testVec): #獲取決策樹節點 firstStr = next(iter(inputTree)) #下一個字典 secondDict = inputTree[firstStr] 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 classLabel if __name__ == '__main__': dataSet, Labels = createDataSet() featLabels = [] myTree = createTree(dataSet, Labels, featLabels) testVec = [0, 1] result = classify(myTree, featLabels, testVec) if result == 'yes': print('放貸') else: print('不放貸')