本代碼來源自:https://github.com/Erikfather/Decision_tree-python
1.數據集描述
共分為四個屬性特征:年齡段,有工作,有自己的房子,信貸情況; 現根據這四種屬性特征來決定是否給予貸款

為了方便,我對數據集進行如下處理:
在編寫代碼之前,我們先對數據集進行屬性標注。
(0)年齡:0代表青年,1代表中年,2代表老年;
(1)有工作:0代表否,1代表是;
(2)有自己的房子:0代表否,1代表是;
(3)信貸情況:0代表一般,1代表好,2代表非常好;
(4)類別(是否給貸款):no代表否,yes代表是。
存入txt文件中:

然后分別利用ID3,C4.5,CART三種算法對數據集進行決策樹分類;
數據集的讀取:
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 = ['年齡段','有工作','有自己的房子','信貸情況']
dataset = []
for line in all_lines[0:]:
line = line.strip().split(',') #以逗號為分割符拆分列表
dataset.append(line)
return dataset,labels
運行一下,看看有什么效果:
dataset,labels = read_dataset('./data/dataset.txt')
print(dataset,labels)
[['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']] ['年齡段', '有工作', '有自己的房子', '信貸情況']
2. 計算信息熵

def inforEntropy(dataset):
m = len(dataset) #數據集的長度
labelCounts = {} #給所有可能分類創建字典
for featvec in dataset:
currentlabel = featvec[-1] #獲取當前樣本的label
if currentlabel not in labelCounts.keys():
labelCounts[currentlabel] = 0
labelCounts[currentlabel] += 1 # 統計每類標簽的樣本個數
Ent=0.0
for key in labelCounts:
p = float(labelCounts[key]) / m
Ent = Ent - p*log(p,2)
return Ent
3. ID3算法的實現
def ID3(dataset):
numFeatures = len(dataset[0])-1 #特征的個數
baseEnt = inforEntropy(dataset) #沒划分之前的信息熵
bestInfoGain = 0.0 #信息增益先定義一下
bestFeature = -1 # 最優的特征先定義一下
for i in range(numFeatures): #遍歷所有的特征
featList = [example[i] for example in dataset] #只取出每個樣本的第i個特征 創建一個列表
uniqueVals = set(featList) #將特征列表創建成為set集合,元素不可重復。創建唯一的分類標簽列表
newEnt = 0.0
for value in uniqueVals: #計算每種划分方式的信息熵
subdataset = splitdataset(dataset,i,value) #根據某一屬性i取出樣本集中這一屬性值等於value的樣本
p = len(subdataset) / float(len(dataset)) #計算|D(v)| / |D|
newEnt += p*inforEntropy(subdataset) #計算划分后的信息熵
inforGain = baseEnt - newEnt #計算信息增益
print(u"ID3中第%d個特征的信息增益為:%.3f"%(i,inforGain))
if inforGain > bestInfoGain:
bestInfoGain = inforGain
bestFeature = i
return bestFeature
3.1 利用ID3算法創建決策樹
首先明白樹停止生長的條件:
1. 當前節點包含的樣本全屬於同一類別,無需划分
2. 當前屬性集為空,或者所有樣本在所有屬性上的取值相同,但所屬的類別不同,無法划分
3. 當前節點包含的樣本集為空,不能划分
def majorityCnt(classList):
classCont = {}
for vote in classList:
if vote not in classList.keys():
classCont[vote] = 0
classCont[vote] += 1 #每個類別統計一下個數
sortedClassCont = sorted(classCont.items(),key=operator.itemgetter(1),reverse=True) #此處要注意sorted的用法和其中的key參數
return sortedClassCont[0][0]
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(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
4 .創建完決策樹后進行分類
def classify(inputTree,featLables,testdataset):
"""
輸入:決策樹,分類標簽,測試數據
輸出:決策結果
描述:跑決策樹
"""
firstStr = list(inputTree.keys())[0]
secondDict = inputTree[firstStr]
featIndex = featLables.index(firstStr)
classlabel = '0'
for key in secondDict.keys():
if testdataset[featIndex] == key:
if type(secondDict[key]).__name__=='dict':
classlabel = classify(secondDict[key],featLables,testdataset)
else:
classlabel = secondDict[key]
return classlabel
def classifytest(inputTree, featLabels, testDataSet):
"""
輸入:決策樹,分類標簽,測試數據集
輸出:決策結果
描述:跑決策樹
"""
classLabelAll = []
for testVec in testDataSet:
classLabelAll.append(classify(inputTree, featLabels, testVec))
return classLabelAll
5.主程序:
if __name__ == "__main__":
filename='./data/dataset.txt'
testfile='./data/testset.txt'
dataset, labels = read_dataset(filename)
#dataset,features=createDataSet()
print ('dataset',dataset)
print("---------------------------------------------")
print(u"數據集長度",len(dataset))
print ("Ent(D):",inforEntropy(dataset))
print("---------------------------------------------")
print(u"以下為首次尋找最優索引:\n")
print(u"ID3算法的最優特征索引為:"+str(ID3(dataset)))
print ("--------------------------------------------------")
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("---------------------------------------------")
if dec_tree=='q':
break
6. 運行結果

