決策樹⑤——Python代碼實現決策樹 Decision_tree-python(ID3,C4.5,CART)

Decision_tree-python

決策樹分類(ID3,C4.5,CART)

三種算法的區別如下:

(1) ID3算法以信息增益爲準則來進行選擇劃分屬性,選擇信息增益最大的;
(2) C4.5算法先從候選劃分屬性中找出信息增益高於平均水平的屬性,再從中選擇增益率最高的;
(3) CART算法使用“基尼指數”來選擇劃分屬性,選擇基尼值最小的屬性作爲劃分屬性.

本次實驗我的數據集如下所示:

共分爲四個屬性特徵:年齡段,有工作,有自己的房子,信貸情況;

現根據這四種屬性特徵來決定是否給予貸款

爲了方便,我對數據集進行如下處理:

在編寫代碼之前,我們先對數據集進行屬性標註。

(0)年齡:0代表青年,1代表中年,2代表老年;

(1)有工作:0代表否,1代表是;

(2)有自己的房子:0代表否,1代表是;

(3)信貸情況:0代表一般,1代表好,2代表非常好;

(4)類別(是否給貸款):no代表否,yes代表是。

存入txt文件中:

然後分別利用ID3,C4.5,CART三種算法對數據集進行決策樹分類;

具體代碼見:tree.py和treePlotter.py

實驗結果如下:

(1)先將txt中數據讀入數組,並打印出數據集長度,即共有多少條數據,並計算出起始信息熵Ent(D),然後分別找出三種算法的首個最優特徵索引

(2)下面通過相應的最優劃分屬性分別創建相應的決策樹,並對測試集進行測試,測試集如下:

結果如下:

ID3算法:

C4.5算法:
CART算法:

由上面結果可以看出:

(1)ID3和C4.5的最優索引以及決策樹形圖是相同的,而CART的最優索引以及決策樹形圖與前面兩者不同,這與它們的選擇標準以及訓練集有關;

(2)但同時我們也發現,三種算法對測試集的測試結果是相同的,經過後期手動匹配,結果完全正確,這說明我們的決策樹實驗結果是正確的。

完整代碼

# %load tree.py

from math import log
import operator
i
mport treePlotter

def read_dataset(filename):
    """
    年齡段:0代表青年,1代表中年,2代表老年;
    有工作:0代表否,1代表是;
    有自己的房子:0代表否,1代表是;
    信貸情況:0代表一般,1代表好,2代表非常好;
    類別(是否給貸款):0代表否,1代表是
    """
    fr=open(filename,'r')
    all_lines=fr.readlines()   #list形式,每行爲1個str
    #print all_lines
    labels=['年齡段', '有工作', '有自己的房子', '信貸情況'] 
    #featname=all_lines[0].strip().split(',')  #list形式
    #featname=featname[:-1]
    labelCounts={}
    dataset=[]
    for line in all_lines[0:]:
        line=line.strip().split(',')   #以逗號爲分割符拆分列表
        dataset.append(line)
    return dataset,labels

def read_testset(testfile):
    """
    年齡段:0代表青年,1代表中年,2代表老年;
    有工作:0代表否,1代表是;
    有自己的房子:0代表否,1代表是;
    信貸情況:0代表一般,1代表好,2代表非常好;
    類別(是否給貸款):0代表否,1代表是
    """
    fr=open(testfile,'r')
    all_lines=fr.readlines()
    testset=[]
    for line in all_lines[0:]:
        line=line.strip().split(',')   #以逗號爲分割符拆分列表
        testset.append(line)
    return testset

#計算信息熵
def jisuanEnt(dataset):
    numEntries=len(dataset)
    labelCounts={}
    #給所有可能分類創建字典
    for featVec in dataset:
        currentlabel=featVec[-1]
        if currentlabel not in labelCounts.keys():
            labelCounts[currentlabel]=0
        labelCounts[currentlabel]+=1
    Ent=0.0
    for key in labelCounts:
        p=float(labelCounts[key])/numEntries
        Ent=Ent-p*log(p,2)#以2爲底求對數
    return Ent

#劃分數據集
def splitdataset(dataset,axis,value):
    retdataset=[]#創建返回的數據集列表
    for featVec in dataset:#抽取符合劃分特徵的值
        if featVec[axis]==value:
            reducedfeatVec=featVec[:axis] #去掉axis特徵
            reducedfeatVec.extend(featVec[axis+1:])#將符合條件的特徵添加到返回的數據集列表
            retdataset.append(reducedfeatVec)
    return retdataset

'''
選擇最好的數據集劃分方式
ID3算法:以信息增益爲準則選擇劃分屬性
C4.5算法:使用“增益率”來選擇劃分屬性
'''
#ID3算法
def ID3_chooseBestFeatureToSplit(dataset):
    numFeatures=len(dataset[0])-1
    baseEnt=jisuanEnt(dataset)
    bestInfoGain=0.0
    bestFeature=-1
    for i in range(numFeatures): #遍歷所有特徵
        #for example in dataset:
            #featList=example[i]  
        featList=[example[i]for example in dataset]
        uniqueVals=set(featList) #將特徵列表創建成爲set集合,元素不可重複。創建唯一的分類標籤列表
        newEnt=0.0
        for value in uniqueVals:     #計算每種劃分方式的信息熵
            subdataset=splitdataset(dataset,i,value)
            p=len(subdataset)/float(len(dataset))
            newEnt+=p*jisuanEnt(subdataset)
        infoGain=baseEnt-newEnt
        print(u"ID3中第%d個特徵的信息增益爲:%.3f"%(i,infoGain))
        if (infoGain>bestInfoGain):
            bestInfoGain=infoGain    #計算最好的信息增益
            bestFeature=i
    return bestFeature   

#C4.5算法
def C45_chooseBestFeatureToSplit(dataset):
    numFeatures=len(dataset[0])-1
    baseEnt=jisuanEnt(dataset)
    bestInfoGain_ratio=0.0
    bestFeature=-1
    for i in range(numFeatures): #遍歷所有特徵
        featList=[example[i]for example in dataset]  
        uniqueVals=set(featList) #將特徵列表創建成爲set集合,元素不可重複。創建唯一的分類標籤列表
        newEnt=0.0
        IV=0.0
        for value in uniqueVals:     #計算每種劃分方式的信息熵
            subdataset=splitdataset(dataset,i,value)
            p=len(subdataset)/float(len(dataset))
            newEnt+=p*jisuanEnt(subdataset)
            IV=IV-p*log(p,2)
        infoGain=baseEnt-newEnt
        if (IV == 0): # fix the overflow bug
            continue
        infoGain_ratio = infoGain / IV                   #這個feature的infoGain_ratio    
        print(u"C4.5中第%d個特徵的信息增益率爲:%.3f"%(i,infoGain_ratio))
        if (infoGain_ratio >bestInfoGain_ratio):          #選擇最大的gain ratio
            bestInfoGain_ratio = infoGain_ratio
            bestFeature = i                              #選擇最大的gain ratio對應的feature
    return bestFeature

#CART算法
def CART_chooseBestFeatureToSplit(dataset):

    numFeatures = len(dataset[0]) - 1
    bestGini = 999999.0
    bestFeature = -1
    for i in range(numFeatures):
        featList = [example[i] for example in dataset]
        uniqueVals = set(featList)
        gini = 0.0
        for value in uniqueVals:
            subdataset=splitdataset(dataset,i,value)
            p=len(subdataset)/float(len(dataset))
            subp = len(splitdataset(subdataset, -1, '0')) / float(len(subdataset))
        gini += p * (1.0 - pow(subp, 2) - pow(1 - subp, 2))
        print(u"CART中第%d個特徵的基尼值爲:%.3f"%(i,gini))
        if (gini < bestGini):
            bestGini = gini
            bestFeature = i
    return bestFeature

def majorityCnt(classList):
    '''
    數據集已經處理了所有屬性,但是類標籤依然不是唯一的,
    此時我們需要決定如何定義該葉子節點,在這種情況下,我們通常會採用多數表決的方法決定該葉子節點的分類
    '''
    classCont={}
    for vote in classList:
        if vote not in classCont.keys():
            classCont[vote]=0
        classCont[vote]+=1
    sortedClassCont=sorted(classCont.items(),key=operator.itemgetter(1),reverse=True)
    return sortedClassCont[0][0]

#利用ID3算法創建決策樹
def ID3_createTree(dataset,labels):
    classList=[example[-1] for example in dataset]
    if classList.count(classList[0]) == len(classList):
        # 類別完全相同,停止劃分
        return classList[0]
    if len(dataset[0]) == 1:
        # 遍歷完所有特徵時返回出現次數最多的
        return majorityCnt(classList)
    bestFeat = ID3_chooseBestFeatureToSplit(dataset)
    bestFeatLabel = labels[bestFeat]
    print(u"此時最優索引爲:"+(bestFeatLabel))
    ID3Tree = {bestFeatLabel:{}}
    del(labels[bestFeat])
    # 得到列表包括節點所有的屬性值
    featValues = [example[bestFeat] for example in dataset]
    uniqueVals = set(featValues)
    for value in uniqueVals:
        subLabels = labels[:]
        ID3Tree[bestFeatLabel][value] = ID3_createTree(splitdataset(dataset, bestFeat, value), subLabels)
    return ID3Tree 

def C45_createTree(dataset,labels):
    classList=[example[-1] for example in dataset]
    if classList.count(classList[0]) == len(classList):
        # 類別完全相同,停止劃分
        return classList[0]
    if len(dataset[0]) == 1:
        # 遍歷完所有特徵時返回出現次數最多的
        return majorityCnt(classList)
    bestFeat = C45_chooseBestFeatureToSplit(dataset)
    bestFeatLabel = labels[bestFeat]
    print(u"此時最優索引爲:"+(bestFeatLabel))
    C45Tree = {bestFeatLabel:{}}
    del(labels[bestFeat])
    # 得到列表包括節點所有的屬性值
    featValues = [example[bestFeat] for example in dataset]
    uniqueVals = set(featValues)
    for value in uniqueVals:
        subLabels = labels[:]
        C45Tree[bestFeatLabel][value] = C45_createTree(splitdataset(dataset, bestFeat, value), subLabels)
    return C45Tree 

def CART_createTree(dataset,labels):
    classList=[example[-1] for example in dataset]
    if classList.count(classList[0]) == len(classList):
        # 類別完全相同,停止劃分
        return classList[0]
    if len(dataset[0]) == 1:
        # 遍歷完所有特徵時返回出現次數最多的
        return majorityCnt(classList)
    bestFeat = CART_chooseBestFeatureToSplit(dataset)
    #print(u"此時最優索引爲:"+str(bestFeat))
    bestFeatLabel = labels[bestFeat]
    print(u"此時最優索引爲:"+(bestFeatLabel))
    CARTTree = {bestFeatLabel:{}}
    del(labels[bestFeat])
    # 得到列表包括節點所有的屬性值
    featValues = [example[bestFeat] for example in dataset]
    uniqueVals = set(featValues)
    for value in uniqueVals:
        subLabels = labels[:]
        CARTTree[bestFeatLabel][value] = CART_createTree(splitdataset(dataset, bestFeat, value), subLabels)
    return CARTTree 

def classify(inputTree, featLabels, testVec):
    """
    輸入:決策樹,分類標籤,測試數據
    輸出:決策結果
    描述:跑決策樹
    """
    firstStr = list(inputTree.keys())[0]
    secondDict = inputTree[firstStr]
    featIndex = featLabels.index(firstStr)
    classLabel = '0'
    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

def classifytest(inputTree, featLabels, testDataSet):
    """
    輸入:決策樹,分類標籤,測試數據集
    輸出:決策結果
    描述:跑決策樹
    """
    classLabelAll = []
    for testVec in testDataSet:
        classLabelAll.append(classify(inputTree, featLabels, testVec))
    return classLabelAll

if __name__ == '__main__':
    filename='dataset.txt'
    testfile='testset.txt'
    dataset, labels = read_dataset(filename)
    #dataset,features=createDataSet()
    print ('dataset',dataset)
    print("---------------------------------------------")
    print(u"數據集長度",len(dataset))
    print ("Ent(D):",jisuanEnt(dataset))
    print("---------------------------------------------")

    print(u"以下爲首次尋找最優索引:\n")
    print(u"ID3算法的最優特徵索引爲:"+str(ID3_chooseBestFeatureToSplit(dataset)))
    print ("--------------------------------------------------")
    print(u"C4.5算法的最優特徵索引爲:"+str(C45_chooseBestFeatureToSplit(dataset)))
    print ("--------------------------------------------------")
    print(u"CART算法的最優特徵索引爲:"+str(CART_chooseBestFeatureToSplit(dataset)))
    print(u"首次尋找最優索引結束!")
    print("---------------------------------------------")

    print(u"下面開始創建相應的決策樹-------")
    
    while(True):    
        dec_tree=str(input("請選擇決策樹:->(1:ID3; 2:C4.5; 3:CART)|('enter q to quit!')|:"))
        #ID3決策樹
        if dec_tree=='1':
            labels_tmp = labels[:] # 拷貝,createTree會改變labels
            ID3desicionTree = ID3_createTree(dataset,labels_tmp)
            print('ID3desicionTree:\n', ID3desicionTree)
            #treePlotter.createPlot(ID3desicionTree)
            treePlotter.ID3_Tree(ID3desicionTree)
            testSet = read_testset(testfile)
            print("下面爲測試數據集結果:")
            print('ID3_TestSet_classifyResult:\n', classifytest(ID3desicionTree, labels, testSet))
            print("---------------------------------------------")
        
        #C4.5決策樹
        if dec_tree=='2':
            labels_tmp = labels[:] # 拷貝,createTree會改變labels
            C45desicionTree =C45_createTree(dataset,labels_tmp)
            print('C45desicionTree:\n', C45desicionTree)
            treePlotter.C45_Tree(C45desicionTree)
            testSet = read_testset(testfile)
            print("下面爲測試數據集結果:")
            print('C4.5_TestSet_classifyResult:\n', classifytest(C45desicionTree, labels, testSet))
            print("---------------------------------------------")
        
        #CART決策樹
        if dec_tree=='3':
            labels_tmp = labels[:] # 拷貝,createTree會改變labels        
            CARTdesicionTree = CART_createTree(dataset,labels_tmp)
            print('CARTdesicionTree:\n', CARTdesicionTree)
            treePlotter.CART_Tree(CARTdesicionTree)
            testSet = read_testset(testfile)
            print("下面爲測試數據集結果:")
            print('CART_TestSet_classifyResult:\n', classifytest(CARTdesicionTree, labels, testSet))
        if dec_tree=='q':
            break
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章