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