03-0004 CART決策樹解決銀行貸款問題(Python)

1.問題描述

The experimental data includes four attribute characteristics: age group,
whether there is work, whether there is a house, credit situation, it is necessary to decide whether to give loans according to these four attribute characteristics.

實驗數據包括四個屬性特徵:年齡組,是否有工作,是否有房屋,信用狀況,根據這四個屬性特徵來決定是否給予貸款。

數據如下:
在這裏插入圖片描述

2.術語解釋

2.1.CART

Classification and Regression Trees,分類與迴歸樹。是由四人幫Leo Breiman, Jerome Friedman, Richard Olshen與Charles Stone於1984年提出,既可用於分類也可用於迴歸。本文將主要介紹用於分類的CART。CART被稱爲數據挖掘領域內里程碑式的算法。

2.2.基尼係數

Gini(p)=k=1Kpk(1pk)=1k=1Kpk2Gini(p)=\sum_{k=1}^{K} p_k(1-p_k)=1-\sum_{k=1}^{K}p_k^2

2.3.基尼指數

Gini(D,A)=D1DGini(D1)+D2DGini(D2)Gini(D,A)={\frac{|D_1|}{D}Gini(D_1)}+{\frac{|D_2|}{D}Gini(D_2)}

2.4.基尼係數增益

Δ=Gini(A)Gini(D,A)\Delta=Gini(A)-Gini(D,A)

3.算法步驟

3.1語言描述

對於以上數據相應的語言描述

  1. 首先計算當前數據的最後一列的基尼係數
  2. 計算每一列每一個屬性的基尼指數,利用公式得到基尼係數增益,選取最大的一個作爲當前類的基尼係數增益。
  3. 從在這麼多基尼係數中選取最大的那個,並且記錄當前列的下標,以及得到最大基尼係數增益的屬性。
  4. 將這一列數據從原始數據中去除,並把該屬性對應的那些行取出來,作爲新的矩陣。回到步驟1。
  5. 當檢測到這個屬性對應的是否貸款全爲yes或者是no的時候停止。[這一步應該是遞歸終止的條件,而遞歸是在構建決策樹的時候纔會使用,以上所述是實驗的思想。 ] [應該是當檢測到這個屬性對應的是否貸款全爲yes或者是no時加入決策樹,如果不是就進行遞歸。]

對於以上步驟,描述的不是很清楚,在第四步,我有點暈,不知道應該怎麼處理,寫代碼的時候決策樹的構建,是參考過來的,並不是特別懂。這篇文章以後還會更新

3.2舉例說明

在實際的計算過程之中,取基尼係數增益最大的項。在這個問題之中,每一計算基尼係數都是針對於最後一列,也就是要區分的那一列。

首先進行數據的統計,以方便處理:

ALL=16ALL=16
AGEyouth=5AGEmiddle=5AGEold=6AGE_{youth}=5,AGE_{middle}=5,AGE_{old}=6
JOBno=11JOByes=5JOB_{no}=11,JOB_{yes}=5
HOUSEno=10HOUSEyes=6HOUSE_{no}=10,HOUSE_{yes}=6
CREDITgeneral=5CREDITgood=6CREDITverygood=5CREDIT_{general}=5,CREDIT_{good}=6,CREDIT_{very good}=5
GLno=7GLyes=9GL_{no}=7,GL_{yes}=9

計算最後一列的基尼係數:
Gini(GL)=1(716)2(916)2=0.4921875Gini(GL)=1-(\frac{7}{16})^{2}-(\frac{9}{16})^{2}=0.4921875
.
AGE

AGE列的每一個屬性計算基尼係數:

Gini(youth)=1(35)2(25)2=0.48Gini(youth)=1-(\frac{3}{5})^{2}-(\frac{2}{5})^{2}=0.48
Gini(notyouth)=1(411)2(711)2=0.4628099173553719Gini(not_{youth})=1-(\frac{4}{11})^{2}-(\frac{7}{11})^{2}=0.4628099173553719
.
Gini(middle)=1(25)2(35)2=0.48Gini(middle)=1-(\frac{2}{5})^{2}-(\frac{3}{5})^{2}=0.48
Gini(notmiddle)=1(511)2(611)2=0.49586776859504145Gini(not_{middle})=1-(\frac{5}{11})^{2}-(\frac{6}{11})^{2}=0.49586776859504145
.
Gini(old)=1(46)2(26)2=0.4444444444444444Gini(old)=1-(\frac{4}{6})^{2}-(\frac{2}{6})^{2}=0.4444444444444444
Gini(notold)=1(510)2(510)2=0.5Gini(not_{old})=1-(\frac{5}{10})^{2}-(\frac{5}{10})^{2}=0.5

AGE列的每個屬性基尼指數:

youth的基尼指數:
Gini(AGE,youth)=AGEyouthAGEallGini(youth)+AGEnotyouthAGEallGini(notyouth)Gini(AGE,youth)={\frac{|AGE_{youth}|}{AGE_{all}}Gini(youth)}+{\frac{|AGE_{not_{youth}}|}{AGE_{all}}Gini(not_{youth})}
.
middle的基尼指數:
Gini(AGE,middle)=AGEmiddleAGEallGini(middle)+AGEnotmiddleAGEallGini(notmiddle)Gini(AGE,middle)={\frac{|AGE_{middle}|}{AGE_{all}}Gini(middle)}+{\frac{|AGE_{not_{middle}}|}{AGE_{all}}Gini(not_{middle})}
.
old的基尼指數:
Gini(AGE,old)=AGEoldAGEallGini(old)+AGEnotoldAGEallGini(notold)Gini(AGE,old)={\frac{|AGE_{old}|}{AGE_{all}}Gini(old)}+{\frac{|AGE_{not_{old}}|}{AGE_{all}}Gini(not_{old})}
.

AGE列的基尼係數增益增益:

Δyouth=Gini(GL)Gini(AGE,youth)=0.0240056818181818\Delta_{youth}=Gini(GL)-Gini(AGE,youth)=0.0240056818181818
Δmiddle=Gini(GL)Gini(AGE,middle)=0.001278409090908983\Delta_{middle}=Gini(GL)-Gini(AGE,middle)=0.001278409090908983
Δold=Gini(GL)Gini(AGE,old)=0.01302083333333337\Delta_{old}=Gini(GL)-Gini(AGE,old)=0.01302083333333337
.
[所以選取第一列,選取增益最大的youth的基尼係數增益最爲第一列的增益]
ΔAGE=Δyouth=0.0240056818181818\Delta_{AGE}=\Delta_{youth}=0.0240056818181818

JOB

JOB每個屬性的基尼係數:

Gini(no)=1(711)2(411)2=0.4628099173553719Gini(no)=1-(\frac{7}{11})^{2}-(\frac{4}{11})^{2}=0.4628099173553719
Gini(yes)=1(55)2(05)2=0.0Gini(yes)=1-(\frac{5}{5})^{2}-(\frac{0}{5})^{2}=0.0
[因爲只有兩類,所以只需要這樣計算一次]

JOB的每個屬性的基尼指數:

[因爲只有兩項,所以基尼指數計算一個即可]
Gini(JOB,no)=JOBnoJOBallGini(no)+JOByesJOBallGini(yes)Gini(JOB,no)={\frac{|JOB_{no}|}{JOB_{all}}Gini(no)}+{\frac{|JOB_{yes}|}{JOB_{all}}Gini(yes)}

JOB的基尼係數增益:

ΔJOB=Gini(GL)Gini(JOB,no)\Delta_{JOB}=Gini(GL)-Gini(JOB,no)
=0.17400568181818182=0.17400568181818182

[此時計算出了兩個增益,進行比較發現JOB的比較大,則最好的增益如下:]
ΔJOB>ΔHOUSE\Delta_{JOB} > \Delta_{HOUSE}
Δ=ΔJOB\Delta=\Delta_{JOB}
按照上面的規則,計算餘下的兩列,每次有更好的增益的時候,就進行替換,並且記住這個增益是來自於哪一列的

4.實驗源碼

在源碼中,註釋特意寫的比較清楚,對於不理解的地方可以輸出一下看一看。對於其中決策樹的構建,我將CARTTree輸出了幾次,爲了看清楚遞歸的流程,如果你保存並且運行了這個源碼,可以試一下。

#!/usr/bin/python
# -*- coding: UTF-8 -*-
import operator
import numpy as np
from numpy import *
from collections import Counter
'''
# introduction:
    The experimental data includes four attribute characteristics: age group, 
    whether there is work, whether there is a house, credit situation, 
    it is necessary to decide whether to give loans according to these 
    four attribute characteristics.
# 數據集
    train_data_set
    ID	AGE	    JOB	HOUSE	CREDIT	    GIVE LOANS
    1	youth	no	no	    general	        no
    2	youth	no	no	    good	        no
    3	youth	yes	no	    good	        yes
    4	youth	yes	yes	    general	        yes
    5	youth	no	no	    general	        no
    6	middle	no	no	    general	        no
    7	middle	no	no	    good	        no
    8	middle	yes	yes	    good	        yes
    9	middle	no	yes	    very good	    yes
    10	middle	no	yes	    very good	    yes
    11	old	    no	yes	    very good	    yes
    12	old	    no	yes	    good	        yes
    13	old	    yes	no	    good	        yes
    14	old	    yes	no	    very good	    yes
    15	old	    no	no	    general	        no
    16	old	    no	no	    very good	    no
# 數據集處理
    (0)	Age: 0 for youth, 1 for middle age, 2 for old age;
	(1)	There is work: 0 means no, 1 means yes;
	(2)	Have your own house: 0 means no, 1 means yes;
	(3)	Credit situation: 0 stands for general, 1 stands for good, 2 stands for very good;
	(4)	Category (whether to give loans): no means no, yes means yes.
'''

# 劃分子集:根據特徵(axis)的屬性值(value)劃分數據集,並返回等於屬性值(value)的子集。
#解釋equal:返回值等於或不等於value的子集,該值進行控制
def splitdataset(dataset,axis,value,isequal):       
    retdataset=[]                                   #定義一個用於存放子集的變量
    length=len(dataset)                             #元素個數獲取,有幾行
    if isequal:                                     #判斷是否相等
        for i in range(length):                     #遍歷
            if dataset[i][axis]==value:             #判斷第一行的下標爲axis的元素是不是等於value
                ret=dataset[i][:axis]               #是的話就將該元素前的所有元素給一個臨時變量ret
                ret.extend(dataset[i][axis+1:])     #並且給這個臨時變量ret添加上axis之後的元素,相當於不要axis列
                retdataset.append(ret)              #將新的一行元素添加到retdataset之中
    else:
        for i in range(length):
            if dataset[i][axis]!=value:
                ret=dataset[i][:axis]
                ret.extend(dataset[i][axis+1:])
                retdataset.append(ret)
    return retdataset                               #返回,這是一個部分,不是全部

# CART:根據基尼係數選擇當前數據集的最優劃分特徵
def CART_chooseBestFeatureToSplit(dataset):
    numFeatures = len(dataset[0]) - 1                           #特徵的數量4
    numrows=len(dataset)                                        #行數,有多少個訓練數據
    bestgini=0                                                  #因爲計算的增益,最後取增益最大的,所以這裏初始爲0
    bestFeature = -1                                            #初始化最佳劃分特徵 -1
    array=mat(dataset)                                          #在CART_gini中有說明
    giniloan=CART_gini(dataset)
    for i in range(numFeatures):                                #循環
        count=Counter(np.transpose(array[:,i]).tolist()[0])
        axisnum=len(count)
        for item in count:
            isDataItem=splitdataset(dataset,i,item[0],True)     #取等於該元素的子集
            notDataItem=splitdataset(dataset,i,item[0],False)   #取不等於等於該元素的字集,因爲是二分,所以必須這麼做
            isDataItemGini=CART_gini(isDataItem)                #獲取子集的基尼係數
            notDataItemGini=CART_gini(notDataItem)              #同上。#下面語句爲計算基尼指數增益的公式,去百度。
            gini=giniloan-int(count[item])/numrows*isDataItemGini-(numrows-int(count[item]))/numrows*notDataItemGini
            if gini>bestgini:                                   #當增益出現新高度的時候,進行刷新
                bestgini=gini
                bestFeature=i
    return bestFeature                                          #返回一個值,是一個int類型的準確的值

# 返回一個dataset的基尼係數(1-something)的形式
def CART_gini(dataset):
    gini=0
    numrows=len(dataset)
    numFeatures = len(dataset[0]) - 1
    array=mat(dataset)                                              #將列表轉化爲矩陣
    dic=Counter(np.transpose(array[:,numFeatures]).tolist()[0])     #取矩陣的第numFeatures列,並轉置成行,然後轉化爲列表,並放入字典dic之中,自動進行統計
    for item in dic:
        gini+=(dic[item]/numrows)**2                                #與下一行一起,是用於計算基尼係數的(基尼係數與基尼指數是兩個不同的概念)
    return 1-gini

#CART決策樹構建
def CART_createTree(dataset, labels):
    classList=[example[-1] for example in dataset]              #取分類標籤(是否放貸:1(yes) or 0(no))
    if classList.count(classList[0])==len(classList):           #如果類別完全相同,則停止繼續劃分
        return classList[0]

    bestFeat=CART_chooseBestFeatureToSplit(dataset)             #選擇最優特徵
    bestFeatLabel=labels[bestFeat]                              #最優特徵的標籤
    CARTTree={bestFeatLabel:{}}                                 #根據最優特徵的標籤生成樹
    del(labels[bestFeat])                                       #刪除已經使用的特徵標籤
    featValues=[example[bestFeat] for example in dataset]       #得到訓練集中所有最優特徵的屬性值
    uniqueVls=set(featValues)                                   #去掉重複的屬性值

    for value in uniqueVls:                                     #遍歷特徵,創建決策樹
        CARTTree[bestFeatLabel][value]=CART_createTree(splitdataset(dataset,bestFeat,value,True),labels)
    return CARTTree

#根據構建好的決策樹以及對應的標籤,對用例進行分類,輸出分類結果0或1,這個函數是TireTree的搜索
def classify(inputTree, featLabels, testVec):
    firstStr=next(iter(inputTree))                                      #首先進入傳進來的樹的根節點,也就是house結點
    secondDict=inputTree[firstStr]                                      #然後定義一個字典,進入根節點的值空間之中,就是第二層花括號,看的時候很容易理解,此時花括號裏面有兩個元素,一個是確定的鍵值對,另一個是鍵-字典對
    featIndex=featLabels.index(firstStr)                                #根據傳進的labels,判斷這個根節點是第幾列的
    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__':
    #數據集處理,這裏的數據是已經處理好的,最後一列的0代表no,1代表yes
    dataset = [['0', '0', '0', '0', '0'], ['0', '0', '0', '1', '0'], ['0', '1', '0', '1', '1'],
               ['0', '1', '1', '0', '1'], ['0', '0', '0', '0', '0'], ['1', '0', '0', '0', '0'],
               ['1', '0', '0', '1', '0'], ['1', '1', '1', '1', '1'], ['1', '0', '1', '2', '1'],
               ['1', '0', '1', '2', '1'], ['2', '0', '1', '2', '1'], ['2', '0', '1', '1', '1'],
               ['2', '1', '0', '1', '1'], ['2', '1', '0', '2', '1'], ['2', '0', '0', '0', '0'],
               ['2', '0', '0', '2', '0']]
    labels = ['age', 'job', 'house', 'credit situation']    #label就是四個標籤,構建決策樹的時候需要使用
    labels_tmp = labels[:]                                  #因爲在CART_createTree()函數裏面會對於labels_tmp進行處理,所以這裏拷貝了一個副本
    inidata='1,1,1,2'                                       #輸入一條數據
    testVec=inidata.split(",")                              #分割
    # testVec=input().split(",")                            #在控制檯進行輸入時的語句,能夠將input的東西分開
    CARTdesicionTree = CART_createTree(dataset, labels_tmp) #構建決策樹
    print(CARTdesicionTree)
    print(classify(CARTdesicionTree, labels, testVec))      #輸出最終的結果
    
    

5.實驗結果&參考

實驗結果
.在這裏插入圖片描述

參考
1.決策樹之 CART
2.CART樹算法詳解
3.信息熵與基尼係數
4.Cmd Markdown 公式指導手冊
5.機器學習實戰(三)——決策樹
6.【十大經典數據挖掘算法】CART
7.基尼係數(Gini coefficient),或稱洛倫茨係數

6.總結

關於遞歸,依舊不會。需要學習,繼續努力。不妨今天晚上就看一看,把遞歸學會,以後就不會麻煩了。
此次CART的實驗,其中的思想比較簡單,計算過程很單一,重點是TrieTree遞歸樹的生成以及遍歷,需要模仿着再寫一部分代碼,關於遞歸

今天在玩《侍魂·朧月傳說》,40級的boss真鏡明美奈一直挑不過,到了四十五級還是打不過,大家都是弓手爲什麼不能禮讓一下呢?
原來都是七管血扣完就死了,但是最後一次的時候,我似乎觸發了一個Bug,真鏡明美奈入場放大的方式大概有三種:
1.三個有角度偏移的弓箭,然後一個後瞄遠程技能,然後扇面攻擊。這一套比較好躲。
2.先是扇面攻擊,接着後瞄遠程技能,然後就開始滑步沖人。
3.扇面攻擊,滑步沖人。
大概就這樣。今天打的時候,先是奪了第一波攻擊,扣了對方三個血條,還有十三個,然後她放了扇面攻擊,就想滑步衝過來。
但是,後一個技能有判定,大概是我跳起,或者是一定距離內都會觸發,否則:等待。[這個技能放不出來,就一直等待。]
我就離的遠遠的,用遠程小技能跟普攻,站着不動打了對方13管血。O(∩_∩)O哈哈~。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章