http://www.javashuo.com/article/p-faoywzla-et.html算法
CART算法的全稱是Classification And Regression Tree,採用的是Gini指數(選Gini指數最小的特徵s)做爲分裂標準,同時它也是包含後剪枝操做。ide
ID3算法和C4.5算法雖然在對訓練樣本集的學習中能夠儘量多地挖掘信息,但其生成的決策樹分支較大,規模較大。函數
爲了簡化決策樹的規模,提升生成決策樹的效率,就出現了根據GINI係數來選擇測試屬性的決策樹算法CART。學習
CART分類與迴歸樹本質上是同樣的,構建過程都是逐步分割特徵空間,預測過程都是從根節點開始一層一層的判斷直到葉節點給出預測結果。測試
只不過度類樹給出離散值,而回歸樹給出連續值(一般是葉節點包含樣本的均值),atom
另外分類樹基於Gini指數選取分割點,而回歸樹基於平方偏差選取分割點。spa
在ID3算法中咱們使用了信息增益來選擇特徵,信息增益大的優先選擇。在C4.5算法中,採用了信息增益比來選擇特徵,以減小信息增益容易選擇特徵值多的特徵的問題。可是不管是ID3仍是C4.5,都是基於信息論的熵模型的,這裏面會涉及大量的對數運算。.net
CART分類樹算法使用基尼係數來代替信息增益比,基尼係數表明了模型的不純度,基尼係數越小,則不純度越低,特徵越好。這和信息增益(比)是相反的。3d
假設有數據集 ,且
有
個分類,那麼可定義基尼指數爲:
從公式能夠看到,基尼指數的意義是:從數據集D中隨機抽取兩個樣本,其類別不一樣的機率。直覺地,基尼指數越小,則數據集D的純度越高。
若是訓練數據集D根據特徵A是否取某一可能值a被分割爲D1和D2兩部分,則在特徵A的條件下,集合D的基尼指數定義爲:
基尼指數Gini(D)表示集合D的不肯定性,基尼指數Gini(D,A)表示通過A=a分割後集合D的不肯定性。基尼指數越大,樣本的不肯定性也就越大。
相對於用信息增益/信息增益率來做爲決策指標(含log操做),基尼指數的運算量比較小,也很易於理解,這是cart算法使用基尼指數的主要目的。
對於CART分類樹連續值的處理問題,其思想和C4.5是相同的,都是將連續的特徵離散化。
惟一的區別在於在選擇劃分點時的度量方式不一樣,C4.5使用的是信息增益比,則CART分類樹使用的是基尼係數。
具體的思路以下,好比m個樣本的連續特徵A有m個,從小到大排列爲a1,a2,...,am,則CART算法取相鄰兩樣本值的平均數,一共取得m-1個劃分點。
其中第i個劃分點Ti表示爲:Ti=(ai+ai+1)/2。
對於這m-1個點,分別計算以該點做爲二元分類點時的基尼係數。
選擇基尼係數最小的點做爲該連續特徵的二元離散分類點。
好比取到的基尼係數最小的點爲at,則小於at的值爲類別1,大於at的值爲類別2,這樣咱們就作到了連續特徵的離散化。
要注意的是,與ID3或者C4.5處理離散屬性不一樣的是,若是當前節點爲連續屬性,則該屬性後面還能夠參與子節點的產生選擇過程。
對於CART分類樹離散值的處理問題,採用的思路是不停的二分離散特徵。
回憶下ID3或者C4.5,若是某個特徵A被選取創建決策樹節點,若是它有A1,A2,A3三種類別,咱們會在決策樹上一下創建一個三叉的節點。這樣致使決策樹是多叉樹。可是CART分類樹使用的方法不一樣,他採用的是不停的二分。
仍是這個例子,CART分類樹會考慮把A分紅{A1}和{A2,A3}, {A2}和{A1,A3}, {A3}和{A1,A2}三種狀況,找到基尼係數最小的組合,好比{A2}和{A1,A3},而後創建二叉樹節點,一個節點是A2對應的樣本,另外一個節點是{A1,A3}對應的節點。
同時,因爲此次沒有把特徵A的取值徹底分開,後面咱們還有機會在子節點繼續選擇到特徵A來劃分A1和A3。這和ID3或者C4.5不一樣,在ID3或者C4.5的一棵子樹中,離散特徵只會參與一次節點的創建。
https://www.cnblogs.com/ssyfj/p/13229743.html
import numpy as np def calcGini(data_y): #根據基尼指數的定義,根據當前數據集中不一樣標籤類出現次數,獲取當前數據集D的基尼指數 m = data_y.size #獲取所有數據數量 labels = np.unique(data_y) #獲取全部標籤值類別(去重後) gini = 1.0 #初始基尼係數 for i in labels: #遍歷每個標籤值種類 y_cnt = data_y[np.where(data_y==i)].size / m #出現機率 gini -= y_cnt**2 #基尼指數 return gini
測試:
print(calcGini(np.array([1,1,2,3,2,2,1,1,3])))
def splitDataSet(data_X,data_Y,fea_axis,fea_val): #根據特徵、和該特徵下的特徵值種類,實現切分數據集和標籤 #根據僞算法能夠知道,咱們要將數據集劃分爲2部分:特徵值=a和特徵值不等於a eqIdx = np.where(data_X[:,fea_axis]==fea_val) neqIdx = np.where(data_X[:,fea_axis]!=fea_val) return data_X[eqIdx],data_Y[eqIdx],data_X[neqIdx],data_Y[neqIdx]
def chooseBestFeature(data_X,data_Y): #遍歷全部特徵和特徵值,選取最優劃分 m,n = data_X.shape bestFeature = -1 bestFeaVal = -1 minFeaGini = np.inf for i in range(n): #遍歷全部特徵 fea_cls = np.unique(data_X[:,i]) #獲取該特徵下的全部特徵值 # print("{}---".format(fea_cls)) for j in fea_cls: #遍歷全部特徵值 newEqDataX,newEqDataY,newNeqDataX,newNeqDataY=splitDataSet(data_X,data_Y,i,j) #進行數據集切分 feaGini = 0 #計算基尼指數 feaGini += newEqDataY.size/m*calcGini(newEqDataY) + newNeqDataY.size/m*calcGini(newNeqDataY) if feaGini < minFeaGini: bestFeature = i bestFeaVal = j minFeaGini = feaGini return bestFeature,bestFeaVal #返回最優劃分方式
def createTree(data_X,data_Y,fea_idx): #建立決策樹 y_labels = np.unique(data_Y) #1.若是數據集中,全部實例都屬於同一類,則返回 if y_labels.size == 1: return data_Y[0] #2.若是特徵集爲空,表示遍歷了全部特徵,使用多數投票進行決定 if data_X.shape[1] == 0: bestFea,bestCnt = 0,0 for i in y_labels: cnt = data_Y[np.where(data_Y==i)].size if cnt > bestCnt: bestFea = i bestCnt = cnt return bestFea #按照基尼指數,選擇特徵,進行繼續遞歸建立樹 bestFeature, bestFeaVal = chooseBestFeature(data_X,data_Y) # print(bestFeature,bestFeaVal) feaBestIdx = fea_idx[bestFeature] my_tree = {feaBestIdx:{}} #獲取劃分結果 newEqDataX,newEqDataY,newNeqDataX,newNeqDataY = splitDataSet(data_X,data_Y,bestFeature,bestFeaVal) #刪除咱們選擇的最優特徵 newEqDataX = np.delete(newEqDataX,bestFeature,1) newNeqDataX = np.delete(newNeqDataX,bestFeature,1) fea_idx = np.delete(fea_idx,bestFeature,0) my_tree[feaBestIdx]["{}_{}".format(1,bestFeaVal)] = createTree(newEqDataX,newEqDataY,fea_idx) my_tree[feaBestIdx]["{}_{}".format(0,bestFeaVal)] = createTree(newNeqDataX,newNeqDataY,fea_idx) return my_tree
def preDealData(filename): df = pd.read_table(filename,'\t',header = None) columns = ["age","prescript","astigmatic","tearRate"] # df.columns = ["age","prescript","astigmatic","tearRate","Result"] #https://zhuanlan.zhihu.com/p/60248460 #數據預處理,變爲能夠處理的數據 #https://blog.csdn.net/liuweiyuxiang/article/details/78222818 new_df = pd.DataFrame() for i in range(len(columns)): new_df[i] = pd.factorize(df[i])[0] ##factorize函數能夠將Series中的標稱型數據映射稱爲一組數字,相同的標稱型映射爲相同的數字。 data_X = new_df.values data_Y = pd.factorize(df[df.shape[1]-1])[0] #factorize返回的是ndarray類型 data_Y = np.array([data_Y]).T return data_X,data_Y,columns data_X,data_Y,fea_names = preDealData("lenses.txt")
fea_Idx = np.arange(len(fea_names)) print(createTree(data_X,data_Y,fea_Idx))
劃分後:能夠看到選取特徵3中特徵值爲0或者1時,基尼指數值最小,當特徵3中特徵值爲0時,所有是同一類別,因此再也不進行下一步處理。後面只處理特徵值爲1的數據集
劃分後:能夠看到選取特徵2中的特徵值爲0或者1時,出現基尼指數值最小。以此爲劃分依據,分兩種狀況討論
能夠看到選取特徵0中特徵值爲2做爲最優劃分依據,由於基尼指數最小。
能夠看到選取特徵值爲2時,須要進一步劃分。當特徵值爲非2時(0_2),結果爲1,不須要進行下一步處理
能夠看到,對於特徵1中的特徵值0或者1都是純淨數據,不須要進一步劃分。
以上推導知足下面決策:
注意:其中x_y表示是否選取特徵值y。好比圖中1_2,表示選取特徵0下,特徵值選取2的數據集劃分。1_y表示選取這個特徵值,0_y表示選取非該特徵值下的其餘數據集
計算基尼指數:
根據最小基尼指數,咱們選取特徵1中,特徵值爲0進行劃分數據集,因爲特徵1中特徵值爲0時數據爲純淨數據,不須要進一步劃分。對於特徵值爲1,咱們須要進行下一步劃分
根據最小基尼指數值,咱們選取特徵0中,特徵值爲0進行數據集劃分。此時特徵值爲0或者非0狀況下,都是純淨數據,不須要下一步劃分。因而達到下面狀況:
import numpy as np import pandas as pd # 建立數據集 def createDataSet(): dataSet = [[1, 1], [1, 1], [1, 0], [0, 1], [0, 1]] labels = [1, 1, 0, 0, 0] features_names = ['水下', '腳蹼'] # 特徵名稱 return dataSet, labels, features_names def calcGini(data_y): #根據基尼指數的定義,根據當前數據集中不一樣標籤類出現次數,獲取當前數據集D的基尼指數 m = data_y.size #獲取所有數據數量 labels = np.unique(data_y) #獲取全部標籤值類別(去重後) gini = 1.0 #初始基尼係數 for i in labels: #遍歷每個標籤值種類 y_cnt = data_y[np.where(data_y==i)].size / m #出現機率 gini -= y_cnt**2 #基尼指數 return gini def splitDataSet(data_X,data_Y,fea_axis,fea_val): #根據特徵、和該特徵下的特徵值種類,實現切分數據集和標籤 #根據僞算法能夠知道,咱們要將數據集劃分爲2部分:特徵值=a和特徵值不等於a eqIdx = np.where(data_X[:,fea_axis]==fea_val) neqIdx = np.where(data_X[:,fea_axis]!=fea_val) return data_X[eqIdx],data_Y[eqIdx],data_X[neqIdx],data_Y[neqIdx] def chooseBestFeature(data_X,data_Y): #遍歷全部特徵和特徵值,選取最優劃分 m,n = data_X.shape bestFeature = -1 bestFeaVal = -1 minFeaGini = np.inf for i in range(n): #遍歷全部特徵 fea_cls = np.unique(data_X[:,i]) #獲取該特徵下的全部特徵值 # print("{}---".format(fea_cls)) for j in fea_cls: #遍歷全部特徵值 newEqDataX,newEqDataY,newNeqDataX,newNeqDataY=splitDataSet(data_X,data_Y,i,j) #進行數據集切分 feaGini = 0 #計算基尼指數 feaGini += newEqDataY.size/m*calcGini(newEqDataY) + newNeqDataY.size/m*calcGini(newNeqDataY) if feaGini < minFeaGini: bestFeature = i bestFeaVal = j minFeaGini = feaGini return bestFeature,bestFeaVal #返回最優劃分方式 def createTree(data_X,data_Y,fea_idx): #建立決策樹 y_labels = np.unique(data_Y) #1.若是數據集中,全部實例都屬於同一類,則返回 if y_labels.size == 1: return data_Y[0] #2.若是特徵集爲空,表示遍歷了全部特徵,使用多數投票進行決定 if data_X.shape[1] == 0: bestFea,bestCnt = 0,0 for i in y_labels: cnt = data_Y[np.where(data_Y==i)].size if cnt > bestCnt: bestFea = i bestCnt = cnt return bestFea #按照基尼指數,選擇特徵,進行繼續遞歸建立樹 bestFeature, bestFeaVal = chooseBestFeature(data_X,data_Y) # print(bestFeature,bestFeaVal) feaBestIdx = fea_idx[bestFeature] my_tree = {feaBestIdx:{}} #獲取劃分結果 newEqDataX,newEqDataY,newNeqDataX,newNeqDataY = splitDataSet(data_X,data_Y,bestFeature,bestFeaVal) #刪除咱們選擇的最優特徵 newEqDataX = np.delete(newEqDataX,bestFeature,1) newNeqDataX = np.delete(newNeqDataX,bestFeature,1) fea_idx = np.delete(fea_idx,bestFeature,0) my_tree[feaBestIdx]["{}_{}".format(1,bestFeaVal)] = createTree(newEqDataX,newEqDataY,fea_idx) my_tree[feaBestIdx]["{}_{}".format(0,bestFeaVal)] = createTree(newNeqDataX,newNeqDataY,fea_idx) return my_tree def preDealData(filename): df = pd.read_table(filename,'\t',header = None) columns = ["age","prescript","astigmatic","tearRate"] # df.columns = ["age","prescript","astigmatic","tearRate","Result"] #https://zhuanlan.zhihu.com/p/60248460 #數據預處理,變爲能夠處理的數據 #https://blog.csdn.net/liuweiyuxiang/article/details/78222818 new_df = pd.DataFrame() for i in range(len(columns)): new_df[i] = pd.factorize(df[i])[0] ##factorize函數能夠將Series中的標稱型數據映射稱爲一組數字,相同的標稱型映射爲相同的數字。 data_X = new_df.values data_Y = pd.factorize(df[df.shape[1]-1])[0] #factorize返回的是ndarray類型 data_Y = np.array([data_Y]).T return data_X,data_Y,columns data_X,data_Y,fea_names = preDealData("lenses.txt") print(data_X) print(data_Y) # data_x,data_y,fea_names = createDataSet() fea_Idx = np.arange(len(fea_names)) # data_X,data_Y,fea_names = createDataSet() # data_X = np.array(data_X) # data_Y = np.array(data_Y) # # fea_Idx = np.arange(len(fea_names)) print(createTree(data_X,data_Y,fea_Idx))
除了計算基尼指數,其餘大多同ID3算法一致。
young myope no reduced no lenses
young myope no normal soft
young myope yes reduced no lenses
young myope yes normal hard
young hyper no reduced no lenses
young hyper no normal soft
young hyper yes reduced no lenses
young hyper yes normal hard
pre myope no reduced no lenses
pre myope no normal soft
pre myope yes reduced no lenses
pre myope yes normal hard
pre hyper no reduced no lenses
pre hyper no normal soft
pre hyper yes reduced no lenses
pre hyper yes normal no lenses
presbyopic myope no reduced no lenses
presbyopic myope no normal no lenses
presbyopic myope yes reduced no lenses
presbyopic myope yes normal hard
presbyopic hyper no reduced no lenses
presbyopic hyper no normal soft
presbyopic hyper yes reduced no lenses
presbyopic hyper yes normal no lenses