在上一篇文章中,通過調取sklearn庫中的tree子產品來建構在鮑魚資料集上的決策樹,并對測試集鮑魚的年齡進行預測,但是,通過調庫的方式隻能處理數值型的屬性,若資料集中既包含連續型屬性和離散型屬性,則處理起來比較困難。而在本文中,将具體實作決策樹的建構過程,并能分别處理連續型屬性和離散型屬性,最後對 鮑魚資料集 中的鮑魚年齡進行預測。
目錄
前言
一、 資料集
二、步驟
1.引入庫
2.讀入資料
3.資料預處理
4.計算資訊增益
5.劃分資料集
6.計算最優劃分屬性
7.建構決策樹
三.預測
總結
參考文章:
前言
本文将具體實作決策樹的建構過程,并對 鮑魚資料集 中的鮑魚年齡進行預測。
以下是本篇文章正文内容
一、 資料集
訓練集:
測試集:
資料集下載下傳:
訓練集:https://pan.baidu.com/s/1f4yf9vlndVar2J4cLqfjDg 提取碼:U1S1
測試集:https://pan.baidu.com/s/1QQxqDsyoSrs529H49LLg2g 提取碼:U2S2
二、步驟
1.引入庫
代碼如下:(當然有些沒有用到)
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from sklearn.metrics import precision_score
from scipy.stats import multivariate_normal
from mpl_toolkits.mplot3d import Axes3D
import copy
2.讀入資料
代碼如下:
# 讀取資料
train_datas=pd.read_csv(path1,header=None)
test_datas=pd.read_csv(path2,header=None)
path為檔案路徑
3.資料預處理
#轉換成清單
train_data=np.array(train_datas[1:][:]).tolist()
label=train_datas[:1][:]
labels=np.array(label).tolist()[0][:-1]
labelProperties = [0, 1, 1, 1, 1, 1, 1, 1] # 屬性的類型,0表示離散,1表示連續
test_data=test_datas.drop(columns=[8])
#轉換成清單
testData=np.array(test_data[1:][:]).tolist()
4.計算資訊增益
def cal_Ent(dataSet):
# 計算Ent(D)
# 訓練集大小
length=len(dataSet)
# 标簽字典
Ent_D_dict={}
for data in dataSet:
label=data[-1]
if label in Ent_D_dict:
Ent_D_dict[label]+=1
else:
Ent_D_dict[label]=1
Ent_D=0
for i in Ent_D_dict.values():
odd=i/length
Ent_D-=odd*np.log2(odd)
return Ent_D
5.劃分資料集
(連續型屬性):
# 劃分資料集, axis:按第幾個特征劃分, value:劃分特征的值, LorR: value值左側(小于)或右側(大于)的資料集
def splitDataSet_c(dataSet, axis, value, LorR='L'):
retDataSet = []
featVec = []
if LorR == 'L':
for featVec in dataSet:
if float(featVec[axis]) < value:
retDataSet.append(featVec)
else:
for featVec in dataSet:
if float(featVec[axis]) > value:
retDataSet.append(featVec)
return retDataSet
(離散型屬性):
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
6.計算最優劃分屬性
# 選擇最好的資料集劃分方式
def chooseBestFeatureToSplit_c(dataSet, labelProperty):
numFeatures = len(labelProperty) # 特征數
baseEntropy = cal_Ent(dataSet) # 計算根節點的資訊熵
bestInfoGain = 0.0
bestFeature = -1
bestPartValue = None # 連續的特征值,最佳劃分值
for i in range(numFeatures): # 對每個特征循環
featList = [example[i] for example in dataSet]
uniqueVals = set(featList) # 該特征包含的所有值
newEntropy = 0.0
bestPartValuei = None
if labelProperty[i] == 0: # 對離散的特征
for value in uniqueVals: # 對每個特征值,劃分資料集, 計算各子集的資訊熵
subDataSet = splitDataSet(dataSet, i, value)
prob = len(subDataSet) / float(len(dataSet))
newEntropy += prob * cal_Ent(subDataSet)
else: # 對連續的特征
sortedUniqueVals = list(uniqueVals) # 對特征值排序
sortedUniqueVals.sort()
listPartition = []
minEntropy = float("inf")
for j in range(len(sortedUniqueVals) - 1): # 計算劃分點
partValue = (float(sortedUniqueVals[j]) + float(
sortedUniqueVals[j + 1])) / 2
# 對每個劃分點,計算資訊熵
dataSetLeft = splitDataSet_c(dataSet, i, partValue, 'L')
dataSetRight = splitDataSet_c(dataSet, i, partValue, 'R')
probLeft = len(dataSetLeft) / float(len(dataSet))
probRight = len(dataSetRight) / float(len(dataSet))
Entropy = probLeft * cal_Ent(
dataSetLeft) + probRight * cal_Ent(dataSetRight)
if Entropy < minEntropy: # 取最小的資訊熵
minEntropy = Entropy
bestPartValuei = partValue
newEntropy = minEntropy
infoGain = baseEntropy - newEntropy # 計算資訊增益
print("第%d個特征的增益為%.3f" % (i, infoGain)) #列印每個特征的資訊增益
if infoGain > bestInfoGain: # 取最大的資訊增益對應的特征
bestInfoGain = infoGain
bestFeature = i
bestPartValue = bestPartValuei
return bestFeature, bestPartValue
def majorityCnt(classList):
classCount = {}
for vote in classList: #統計classList中每個元素出現的次數
if vote not in classCount.keys():
classCount[vote] = 0
classCount[vote] += 1
sortedClassCount = sorted(classCount.items(), key = operator.itemgetter(1), reverse = True) #根據字典的值降序排序
return sortedClassCount[0][0]
7.建構決策樹
# 建立樹, 樣本集 特征 特征屬性(0 離散, 1 連續)
def createTree_c(dataSet, labels, labelProperty):
print("---------------------------------------------")
# print dataSet, labels, labelProperty
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, bestPartValue = chooseBestFeatureToSplit_c(dataSet,
labelProperty) # 最優分類特征的索引
if bestFeat == -1: # 如果無法選出最優分類特征,傳回出現次數最多的類别
return majorityCnt(classList)
if labelProperty[bestFeat] == 0: # 對離散的特征
bestFeatLabel = labels[bestFeat]
myTree = {bestFeatLabel: {}}
labelsNew = copy.copy(labels)
labelPropertyNew = copy.copy(labelProperty)
del (labelsNew[bestFeat]) # 已經選擇的特征不再參與分類
del (labelPropertyNew[bestFeat])
featValues = [example[bestFeat] for example in dataSet]
uniqueValue = set(featValues) # 該特征包含的所有值
for value in uniqueValue: # 對每個特征值,遞歸建構樹
subLabels = labelsNew[:]
subLabelProperty = labelPropertyNew[:]
myTree[bestFeatLabel][value] = createTree_c(
splitDataSet(dataSet, bestFeat, value), subLabels,
subLabelProperty)
else: # 對連續的特征,不删除該特征,分别建構左子樹和右子樹
bestFeatLabel = labels[bestFeat] + '<' + str(bestPartValue)
myTree = {bestFeatLabel: {}}
subLabels = labels[:]
subLabelProperty = labelProperty[:]
# 建構左子樹
valueLeft = '是'
myTree[bestFeatLabel][valueLeft] = createTree_c(
splitDataSet_c(dataSet, bestFeat, bestPartValue, 'L'), subLabels,
subLabelProperty)
# 建構右子樹
valueRight = '否'
myTree[bestFeatLabel][valueRight] = createTree_c(
splitDataSet_c(dataSet, bestFeat, bestPartValue, 'R'), subLabels,
subLabelProperty)
return myTree
Trees = createTree_c(train_data, labels, labelProperties)
運作結果:
三.預測
# 測試算法
def classify_c(inputTree, featLabels, featLabelProperties, testVec):
firstStr = list(inputTree.keys())[0] # 根節點
firstLabel = firstStr
lessIndex = str(firstStr).find('<')
if lessIndex > -1: # 如果是連續型的特征
firstLabel = str(firstStr)[:lessIndex]
secondDict = inputTree[firstStr]
featIndex = featLabels.index(firstLabel) # 跟節點對應的特征
classLabel = None
for key in secondDict.keys(): # 對每個分支循環
if featLabelProperties[featIndex] == 0: # 離散的特征
if testVec[featIndex] == key: # 測試樣本進入某個分支
if type(secondDict[key]).__name__ == 'dict': # 該分支不是葉子節點,遞歸
classLabel = classify_c(secondDict[key], featLabels,
featLabelProperties, testVec)
else: # 如果是葉子, 傳回結果
classLabel = secondDict[key]
else:
partValue = float(str(firstStr)[lessIndex + 1:])
# print(testVec[featIndex],partValue)
if float(testVec[featIndex]) < partValue: # 進入左子樹
if type(secondDict['是']).__name__ == 'dict': # 該分支不是葉子節點,遞歸
classLabel = classify_c(secondDict['是'], featLabels,
featLabelProperties, testVec)
else: # 如果是葉子, 傳回結果
classLabel = secondDict['是']
else:
if type(secondDict['否']).__name__ == 'dict': # 該分支不是葉子節點,遞歸
classLabel = classify_c(secondDict['否'], featLabels,
featLabelProperties, testVec)
else: # 如果是葉子, 傳回結果
classLabel = secondDict['否']
return classLabel
for i in range(len(testData)):
testClass = classify_c(Trees, labels, labelProperties, testData[i])
print("第",i,"條測試資料的預測ring為:",testClass)
與調庫的方法進行對比:
參考上一篇文章:
https://blog.csdn.net/qq_53644346/article/details/125101128?spm=1001.2014.3001.55yixai
https://blog.csdn.net/qq_53644346/article/details/125101128?spm=1001.2014.3001.5501
調庫預測的結果:
發現結果一樣!
總結
以上就是實作決策樹算法的所有内容,當然以上算法也可以處理其他的資料集,無論是離散屬性和連續屬性都可以處理。