在上篇文章中介紹了決策樹的一些基本概念,在這篇文章中,將介紹決策樹ID3和C4.5算法的代碼實現以及一些優化。html
ID3算法的核心是在決策樹各個節點上應用信息增益準則選擇特徵,遞歸地構建決策樹,算法僞代碼在上篇文章中給出了。在這裏將給出其代碼實現。python
import pandas as pd from math import log def load(filename): ''' input: 文件 output: DataFrame數據 ''' df = pd.read_csv(filename) return df def calEnt(df): ''' input: 數據集 output: 熵 descripion: 計算給定數據集的熵 ''' # 返回數據行數 numEntries = df.shape[0] # 字典,用於保存每一個標籤(label)出現次數 labelCounts = {} cols = df.columns.tolist() # 獲取標籤 classlabel = df[cols[-1]].tolist() # 對每組特徵向量進行統計 for currentlabel in classlabel: if currentlabel not in labelCounts.keys(): labelCounts[currentlabel] = 1 else: labelCounts[currentlabel] += 1 Ent = 0.0 # 計算熵 for key in labelCounts: prob = labelCounts[key]/numEntries Ent -= prob*log(prob, 2) return Ent def splitDatsSet(df, axis, value): ''' input: 數據集,所佔列,選擇值 output: 劃分數據集 description: 按照給定特徵劃分數據集;選擇所佔列中等於選擇值的項 ''' cols = df.columns.tolist() axisFeat = df[axis].tolist() # L = [] # L.append(axis) # retDataset = df[list(set(cols)-set(L))] retDataset = pd.concat([df[feaVec] for feaVec in cols if feaVec != axis], axis=1) i = 0 # 須要丟棄的行 dropIndex = [] for feaVec in axisFeat: if feaVec != value: dropIndex.append(i) i += 1 else: i += 1 # 劃分數據集 newDataset = retDataset.drop(dropIndex) return newDataset.reset_index(drop=True) def chooseBestFeatureToSplit(df): ''' input: 數據集 output: 最優特徵和信息增益 ''' # 獲取特徵數量 numFeatures = len(df.columns) - 1 # 計算熵 Ent = calEnt(df) # 信息增益 bestInfoGain = 0.0 # 最優特徵索引值 bestFeature = -1 cols = df.columns.tolist() # 遍歷全部特徵 for i in range(numFeatures): # 獲取第i個特徵的全部不一樣的取值 equalVals = set(df[cols[i]].tolist()) # 經驗條件熵 newEnt = 0.0 # 計算信息增益 for value in equalVals: # 獲取劃分後的子集 subDataset = splitDatsSet(df, cols[i], value) # 計算子集機率 prob = subDataset.shape[0] / df.shape[0] # 根據公式計算條件熵 newEnt += prob * calEnt(subDataset) infoGain = Ent - newEnt print(cols[i], infoGain) # 更新信息增益,找到最大的信息增益 if infoGain > bestInfoGain: bestInfoGain = infoGain bestFeature = cols[i] return bestFeature, bestInfoGain def majorityCnt(classList): ''' input: 類別列表 output: 子節點的分類 description: 數據集已經處理了全部屬性,可是類標籤依然不是惟一的, 採用多數判決的方法決定該子節點的分類 ''' classCount = {} # 統計classList中每一個元素出現的次數 for clas in classList: if clas not in classCount.keys(): classCount[clas] = 0 classCount[clas] += 1 # 根據字典值降序排列 sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reversed=True) return sortedClassCount[0][0] def createTree(df, dropcol): ''' input: 數據集和需刪除特徵 output: 決策樹 description: 遞歸實現決策樹構建 ''' # cols = df.columns.tolist()[:-1] # 取分類標籤 classList = df[df.columns.tolist()[-1]].tolist() # 若數據集中全部實例屬於同一類,將Ck做爲該節點的類標記 if classList.count(classList[0]) == len(classList): return classList[0] # 若特徵集爲空集,將數據集中實例數最大的類Ck做爲該節點的類標記 if len(df[0:1]) == 0: return majorityCnt(classList) # 獲取最優特徵與信息增益 bestFeature, bestInfoGain = chooseBestFeatureToSplit(df) print('特徵集和類別:',df.columns.tolist()) print('bestFeature:',bestFeature) # 根據最優特徵的標籤生成樹 myTree = {bestFeature:{}} # 獲得最優特徵的屬性值 featValues = df[bestFeature] # 去掉重複屬性值 uniqueVals = set(featValues) # 遍歷建立決策樹 for value in uniqueVals: myTree[bestFeature][value] = createTree(splitDatsSet(df, bestFeature, value), bestFeature) return myTree def main(): filename = "data.csv" dataset = load(filename) dropCol = [] myTree = createTree(dataset, dropCol) print(myTree) if __name__ == '__main__': main()
輸入選取統計學習方法中給出的數據集算法
age,work,hourse,loan,class 青年,否,否,通常,否 青年,否,否,好,否 青年,是,否,好,是 青年,是,是,通常,是 青年,否,否,通常,否 中年,否,否,通常,否 中年,否,否,好,否 中年,是,是,好,是 中年,否,是,很是好,是 中年,否,是,很是好,是 老年,否,是,很是好,是 老年,否,是,好,是 老年,是,否,好,是 老年,是,否,很是好,是 老年,否,否,通常,否
age 0.08300749985576883 work 0.32365019815155627 hourse 0.4199730940219749 loan 0.36298956253708536 特徵集和類別: ['age', 'work', 'hourse', 'loan', 'class'] bestFeature: hourse age 0.2516291673878229 work 0.9182958340544896 loan 0.47385138961004514 特徵集和類別: ['age', 'work', 'loan', 'class'] bestFeature: work {'hourse': {'是': '是', '否': {'work': {'是': '是', '否': '否'}}}}
C4.5就是在上面提出ID3的缺點中的第4條進行的改正,大部分代碼都相同,除了在劃分數據集的時候以信息增益比
爲劃分標準,這在上篇文章中也提到過。app
def chooseBestFeatureToSplit(df): ''' input: 數據集 output: 最優特徵和信息增益 ''' # 獲取特徵數量 numFeatures = len(df.columns) - 1 # 計算熵 Ent = calEnt(df) # 信息增益 bestInfoGain = 0.0 splitInfo = 0.0 # 最優特徵索引值 bestFeature = -1 cols = df.columns.tolist() # 遍歷全部特徵 for i in range(numFeatures): # 獲取第i個特徵的全部不一樣的取值 equalVals = set(df[cols[i]].tolist()) # 經驗條件熵 newEnt = 0.0 # 計算信息增益 for value in equalVals: # 獲取劃分後的子集 subDataset = splitDatsSet(df, cols[i], value) # 計算子集機率 prob = subDataset.shape[0] / df.shape[0] # 根據公式計算條件熵 newEnt += prob * calEnt(subDataset) # 計算特徵熵 splitInfo += -prob * log(prob, 2) infoGain = Ent - newEnt print(cols[i], infoGain) # 計算信息增益比 infoGainRatio = infoGain / splitInfo # 更新信息增益比,找到最大的信息增益比 if infoGainRatio > bestInfoGain: bestInfoGain = infoGainRatio bestFeature = cols[i] return bestFeature, infoGainRatio
這篇文章給出了ID3與C4.5的代碼實現,同時也提到了其中的一些缺點,對於缺點2中提到的過擬合現象,咱們能夠經過剪枝來簡化模型,從而下降這一現象的產生。對於剪枝的實現及介紹,將在後面介紹CART算法時再講述。機器學習
參考:學習
原文出處:https://www.cnblogs.com/csu-lmw/p/10575897.html優化