日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

决策树挑选西瓜

發布時間:2023/12/20 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 决策树挑选西瓜 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

  • 決策樹概念
  • 信息熵
  • CART算法
  • ID3算法實現
  • 使用sklearn庫實現
  • 總結
  • 參考

決策樹概念

決策樹(Decision Tree)是在已知各種情況發生概率的基礎上,通過構成決策樹來求取凈現值的期望值大于等于零的概率,評價項目風險,判斷其可行性的決策分析方法,是直觀運用概率分析的一種圖解法。由于這種決策分支畫成圖形很像一棵樹的枝干,故稱決策樹。在機器學習中,決策樹是一個預測模型,他代表的是對象屬性與對象值之間的一種映射關系。Entropy = 系統的凌亂程度,使用算法ID3, C4.5和C5.0生成樹算法使用熵。這一度量是基于信息學理論中熵的概念。
決策樹是一種樹形結構,其中每個內部節點表示一個屬性上的測試,每個分支代表一個測試輸出,每個葉節點代表一種類別。
分類樹(決策樹)是一種十分常用的分類方法。它是一種監督學習,所謂監督學習就是給定一堆樣本,每個樣本都有一組屬性和一個類別,這些類別是事先確定的,那么通過學習得到一個分類器,這個分類器能夠對新出現的對象給出正確的分類。這樣的機器學習就被稱之為監督學習。

信息熵

所謂信息熵,是一個數學上頗為抽象的概念,在這里不妨把信息熵理解成某種特定信息的出現概率。而信息熵和熱力學熵是緊密相關的。根據Charles H. Bennett對Maxwell’s Demon的重新解釋,對信息的銷毀是一個不可逆過程,所以銷毀信息是符合熱力學第二定律的。而產生信息,則是為系統引入負(熱力學)熵的過程。所以信息熵的符號與熱力學熵應該是相反的。

CART算法

Classification And Regression Tree,即分類回歸樹算法,簡稱CART算法,它是決策樹的一種實現,通常決策樹主要有三種實現,分別是ID3算法,CART算法和C4.5算法。 CART算法是一種二分遞歸分割技術,把當前樣本劃分為兩個子樣本,使得生成的每個非葉子結點都有兩個分支,因此CART算法生成的決策樹是結構簡潔的二叉樹。由于CART算法構成的是一個二叉樹,它在每一步的決策時只能是“是”或者“否”,即使一個feature有多個取值,也是把數據分為兩部分。在CART算法中主要分為兩個步驟

(1)將樣本遞歸劃分進行建樹過程

(2)用驗證數據進行剪枝

ID3算法實現

修改數據集為

新建.ipynb
代碼:

import numpy as np import pandas as pd import math import collections def import_data():data = pd.read_csv('source\\watermalon.txt')data.head(10)data=np.array(data).tolist()# 特征值列表labels = ['色澤', '根蒂', '敲擊', '紋理', '臍部', '觸感']# 特征對應的所有可能的情況labels_full = {}for i in range(len(labels)):labelList = [example[i] for example in data]uniqueLabel = set(labelList)labels_full[labels[i]] = uniqueLabelreturn data,labels,labels_full data,labels,labels_full=import_data()def calcShannonEnt(dataSet):"""計算給定數據集的信息熵(香農熵):param dataSet::return:"""# 計算出數據集的總數numEntries = len(dataSet)# 用來統計標簽labelCounts = collections.defaultdict(int)# 循環整個數據集,得到數據的分類標簽for featVec in dataSet:# 得到當前的標簽currentLabel = featVec[-1]# # 如果當前的標簽不再標簽集中,就添加進去(書中的寫法)# if currentLabel not in labelCounts.keys():# labelCounts[currentLabel] = 0## # 標簽集中的對應標簽數目加一# labelCounts[currentLabel] += 1# 也可以寫成如下labelCounts[currentLabel] += 1# 默認的信息熵shannonEnt = 0.0for key in labelCounts:# 計算出當前分類標簽占總標簽的比例數prob = float(labelCounts[key]) / numEntries# 以2為底求對數shannonEnt -= prob * math.log2(prob)return shannonEnt print(calcShannonEnt(data)) # 輸出為:0.9975025463691153

得到結果:

之后準備計算信息增益:

def splitDataSet(dataSet, axis, value):"""按照給定的特征值,將數據集劃分:param dataSet: 數據集:param axis: 給定特征值的坐標:param value: 給定特征值滿足的條件,只有給定特征值等于這個value的時候才會返回:return:"""# 創建一個新的列表,防止對原來的列表進行修改retDataSet = []# 遍歷整個數據集for featVec in dataSet:# 如果給定特征值等于想要的特征值if featVec[axis] == value:# 將該特征值前面的內容保存起來reducedFeatVec = featVec[:axis]# 將該特征值后面的內容保存起來,所以將給定特征值給去掉了reducedFeatVec.extend(featVec[axis + 1:])# 添加到返回列表中retDataSet.append(reducedFeatVec)return retDataSet

確定數據集劃分

def chooseBestFeatureToSplit(dataSet, labels):"""選擇最好的數據集劃分特征,根據信息增益值來計算:param dataSet::return:"""# 得到數據的特征值總數numFeatures = len(dataSet[0]) - 1# 計算出基礎信息熵baseEntropy = calcShannonEnt(dataSet)# 基礎信息增益為0.0bestInfoGain = 0.0# 最好的特征值bestFeature = -1# 對每個特征值進行求信息熵for i in range(numFeatures):# 得到數據集中所有的當前特征值列表featList = [example[i] for example in dataSet]# 將當前特征唯一化,也就是說當前特征值中共有多少種uniqueVals = set(featList)# 新的熵,代表當前特征值的熵newEntropy = 0.0# 遍歷現在有的特征的可能性for value in uniqueVals:# 在全部數據集的當前特征位置上,找到該特征值等于當前值的集合subDataSet = splitDataSet(dataSet=dataSet, axis=i, value=value)# 計算出權重prob = len(subDataSet) / float(len(dataSet))# 計算出當前特征值的熵newEntropy += prob * calcShannonEnt(subDataSet)# 計算出“信息增益”infoGain = baseEntropy - newEntropy#print('當前特征值為:' + labels[i] + ',對應的信息增益值為:' + str(infoGain)+"i等于"+str(i))#如果當前的信息增益比原來的大if infoGain > bestInfoGain:# 最好的信息增益bestInfoGain = infoGain# 新的最好的用來劃分的特征值bestFeature = i#print('信息增益最大的特征為:' + labels[bestFeature])return bestFeature

判斷屬性一致性:

def judgeEqualLabels(dataSet):"""判斷數據集的各個屬性集是否完全一致:param dataSet::return:"""# 計算出樣本集中共有多少個屬性,最后一個為類別feature_leng = len(dataSet[0]) - 1# 計算出共有多少個數據data_leng = len(dataSet)# 標記每個屬性中第一個屬性值是什么first_feature = ''# 各個屬性集是否完全一致is_equal = True# 遍歷全部屬性for i in range(feature_leng):# 得到第一個樣本的第i個屬性first_feature = dataSet[0][i]# 與樣本集中所有的數據進行對比,看看在該屬性上是否都一致for _ in range(1, data_leng):# 如果發現不相等的,則直接返回Falseif first_feature != dataSet[_][i]:return Falsereturn is_equal

繪制決策樹并打印:

def createTree(dataSet, labels):"""創建決策樹:param dataSet: 數據集:param labels: 特征標簽:return:"""# 拿到所有數據集的分類標簽classList = [example[-1] for example in dataSet]# 統計第一個標簽出現的次數,與總標簽個數比較,如果相等則說明當前列表中全部都是一種標簽,此時停止劃分if classList.count(classList[0]) == len(classList):return classList[0]# 計算第一行有多少個數據,如果只有一個的話說明所有的特征屬性都遍歷完了,剩下的一個就是類別標簽,或者所有的樣本在全部屬性上都一致if len(dataSet[0]) == 1 or judgeEqualLabels(dataSet):# 返回剩下標簽中出現次數較多的那個return majorityCnt(classList)# 選擇最好的劃分特征,得到該特征的下標bestFeat = chooseBestFeatureToSplit(dataSet=dataSet, labels=labels)print(bestFeat)# 得到最好特征的名稱bestFeatLabel = labels[bestFeat]print(bestFeatLabel)# 使用一個字典來存儲樹結構,分叉處為劃分的特征名稱myTree = {bestFeatLabel: {}}# 將本次劃分的特征值從列表中刪除掉del(labels[bestFeat])# 得到當前特征標簽的所有可能值featValues = [example[bestFeat] for example in dataSet]# 唯一化,去掉重復的特征值uniqueVals = set(featValues)# 遍歷所有的特征值for value in uniqueVals:# 得到剩下的特征標簽subLabels = labels[:]subTree = createTree(splitDataSet(dataSet=dataSet, axis=bestFeat, value=value), subLabels)# 遞歸調用,將數據集中該特征等于當前特征值的所有數據劃分到當前節點下,遞歸調用時需要先將當前的特征去除掉myTree[bestFeatLabel][value] = subTreereturn myTree mytree=createTree(data,labels) print(mytree)


之后繪制可視化樹:

import matplotlib.pylab as plt import matplotlib# 能夠顯示中文 matplotlib.rcParams['font.sans-serif'] = ['SimHei'] matplotlib.rcParams['font.serif'] = ['SimHei']# 分叉節點,也就是決策節點 decisionNode = dict(boxstyle="sawtooth", fc="0.8")# 葉子節點 leafNode = dict(boxstyle="round4", fc="0.8")# 箭頭樣式 arrow_args = dict(arrowstyle="<-")def plotNode(nodeTxt, centerPt, parentPt, nodeType):"""繪制一個節點:param nodeTxt: 描述該節點的文本信息:param centerPt: 文本的坐標:param parentPt: 點的坐標,這里也是指父節點的坐標:param nodeType: 節點類型,分為葉子節點和決策節點:return:"""createPlot.ax1.annotate(nodeTxt, xy=parentPt, xycoords='axes fraction',xytext=centerPt, textcoords='axes fraction',va="center", ha="center", bbox=nodeType, arrowprops=arrow_args)def getNumLeafs(myTree):"""獲取葉節點的數目:param myTree::return:"""# 統計葉子節點的總數numLeafs = 0# 得到當前第一個key,也就是根節點firstStr = list(myTree.keys())[0]# 得到第一個key對應的內容secondDict = myTree[firstStr]# 遞歸遍歷葉子節點for key in secondDict.keys():# 如果key對應的是一個字典,就遞歸調用if type(secondDict[key]).__name__ == 'dict':numLeafs += getNumLeafs(secondDict[key])# 不是的話,說明此時是一個葉子節點else:numLeafs += 1return numLeafsdef getTreeDepth(myTree):"""得到數的深度層數:param myTree::return:"""# 用來保存最大層數maxDepth = 0# 得到根節點firstStr = list(myTree.keys())[0]# 得到key對應的內容secondDic = myTree[firstStr]# 遍歷所有子節點for key in secondDic.keys():# 如果該節點是字典,就遞歸調用if type(secondDic[key]).__name__ == 'dict':# 子節點的深度加1thisDepth = 1 + getTreeDepth(secondDic[key])# 說明此時是葉子節點else:thisDepth = 1# 替換最大層數if thisDepth > maxDepth:maxDepth = thisDepthreturn maxDepthdef plotMidText(cntrPt, parentPt, txtString):"""計算出父節點和子節點的中間位置,填充信息:param cntrPt: 子節點坐標:param parentPt: 父節點坐標:param txtString: 填充的文本信息:return:"""# 計算x軸的中間位置xMid = (parentPt[0]-cntrPt[0])/2.0 + cntrPt[0]# 計算y軸的中間位置yMid = (parentPt[1]-cntrPt[1])/2.0 + cntrPt[1]# 進行繪制createPlot.ax1.text(xMid, yMid, txtString)def plotTree(myTree, parentPt, nodeTxt):"""繪制出樹的所有節點,遞歸繪制:param myTree::param parentPt: 父節點的坐標:param nodeTxt: 節點的文本信息:return:"""# 計算葉子節點數numLeafs = getNumLeafs(myTree=myTree)# 計算樹的深度depth = getTreeDepth(myTree=myTree)# 得到根節點的信息內容firstStr = list(myTree.keys())[0]# 計算出當前根節點在所有子節點的中間坐標,也就是當前x軸的偏移量加上計算出來的根節點的中心位置作為x軸(比如說第一次:初始的x偏移量為:-1/2W,計算出來的根節點中心位置為:(1+W)/2W,相加得到:1/2),當前y軸偏移量作為y軸cntrPt = (plotTree.xOff + (1.0 + float(numLeafs))/2.0/plotTree.totalW, plotTree.yOff)# 繪制該節點與父節點的聯系plotMidText(cntrPt, parentPt, nodeTxt)# 繪制該節點plotNode(firstStr, cntrPt, parentPt, decisionNode)# 得到當前根節點對應的子樹secondDict = myTree[firstStr]# 計算出新的y軸偏移量,向下移動1/D,也就是下一層的繪制y軸plotTree.yOff = plotTree.yOff - 1.0/plotTree.totalD# 循環遍歷所有的keyfor key in secondDict.keys():# 如果當前的key是字典的話,代表還有子樹,則遞歸遍歷if isinstance(secondDict[key], dict):plotTree(secondDict[key], cntrPt, str(key))else:# 計算新的x軸偏移量,也就是下個葉子繪制的x軸坐標向右移動了1/WplotTree.xOff = plotTree.xOff + 1.0/plotTree.totalW# 打開注釋可以觀察葉子節點的坐標變化# print((plotTree.xOff, plotTree.yOff), secondDict[key])# 繪制葉子節點plotNode(secondDict[key], (plotTree.xOff, plotTree.yOff), cntrPt, leafNode)# 繪制葉子節點和父節點的中間連線內容plotMidText((plotTree.xOff, plotTree.yOff), cntrPt, str(key))# 返回遞歸之前,需要將y軸的偏移量增加,向上移動1/D,也就是返回去繪制上一層的y軸plotTree.yOff = plotTree.yOff + 1.0/plotTree.totalDdef createPlot(inTree):"""需要繪制的決策樹:param inTree: 決策樹字典:return:"""# 創建一個圖像fig = plt.figure(1, facecolor='white')fig.clf()axprops = dict(xticks=[], yticks=[])createPlot.ax1 = plt.subplot(111, frameon=False, **axprops)# 計算出決策樹的總寬度plotTree.totalW = float(getNumLeafs(inTree))# 計算出決策樹的總深度plotTree.totalD = float(getTreeDepth(inTree))# 初始的x軸偏移量,也就是-1/2W,每次向右移動1/W,也就是第一個葉子節點繪制的x坐標為:1/2W,第二個:3/2W,第三個:5/2W,最后一個:(W-1)/2WplotTree.xOff = -0.5/plotTree.totalW# 初始的y軸偏移量,每次向下或者向上移動1/DplotTree.yOff = 1.0# 調用函數進行繪制節點圖像plotTree(inTree, (0.5, 1.0), '')# 繪制plt.show()if __name__ == '__main__':createPlot(mytree)

添加標簽重新繪制:

def makeTreeFull(myTree, labels_full, default):"""將樹中的不存在的特征標簽進行補全,補全為父節點中出現最多的類別:param myTree: 生成的樹:param labels_full: 特征的全部標簽:param parentClass: 父節點中所含最多的類別:param default: 如果缺失標簽中父節點無法判斷類別則使用該值:return:"""# 這里所說的父節點就是當前根節點,把當前根節點下不存在的特征標簽作為子節點# 拿到當前的根節點root_key = list(myTree.keys())[0]# 拿到根節點下的所有分類,可能是子節點(好瓜or壞瓜)也可能不是子節點(再次劃分的屬性值)sub_tree = myTree[root_key]# 如果是葉子節點就結束if isinstance(sub_tree, str):return# 找到使用當前節點分類下最多的種類,該分類結果作為新特征標簽的分類,如:色澤下面沒有淺白則用色澤中有的青綠分類作為淺白的分類root_class = []# 把已經分好類的結果記錄下來for sub_key in sub_tree.keys():if isinstance(sub_tree[sub_key], str):root_class.append(sub_tree[sub_key])# 找到本層出現最多的類別,可能會出現相同的情況取其一if len(root_class):most_class = collections.Counter(root_class).most_common(1)[0][0]else:most_class = None# 當前節點下沒有已經分類好的屬性# print(most_class)# 循環遍歷全部特征標簽,將不存在標簽添加進去for label in labels_full[root_key]:if label not in sub_tree.keys():if most_class is not None:sub_tree[label] = most_classelse:sub_tree[label] = default# 遞歸處理for sub_key in sub_tree.keys():if isinstance(sub_tree[sub_key], dict):makeTreeFull(myTree=sub_tree[sub_key], labels_full=labels_full, default=default) makeTreeFull(mytree,labels_full,default='未知') createPlot(mytree)

得到:

使用sklearn庫實現

導入包并讀取:

# 導入包 import pandas as pd from sklearn import tree import graphviz df = pd.read_csv('..\\source\\watermalon.txt') df.head(10)


特征值轉為數字:

df['色澤']=df['色澤'].map({'淺白':1,'青綠':2,'烏黑':3}) df['根蒂']=df['根蒂'].map({'稍蜷':1,'蜷縮':2,'硬挺':3}) df['敲聲']=df['敲聲'].map({'清脆':1,'濁響':2,'沉悶':3}) df['紋理']=df['紋理'].map({'清晰':1,'稍糊':2,'模糊':3}) df['臍部']=df['臍部'].map({'平坦':1,'稍凹':2,'凹陷':3}) df['觸感'] = np.where(df['觸感']=="硬滑",1,2) df['好瓜'] = np.where(df['好瓜']=="是",1,0) x_train=df[['色澤','根蒂','敲聲','紋理','臍部','觸感']] y_train=df['好瓜'] print(df) id3=tree.DecisionTreeClassifier(criterion='entropy') id3=id3.fit(x_train,y_train) print(id3)

得到:

總結

本次實驗學習了解了決策樹,信息熵等的概念,對于ID3,C4.5和CART算法的各優缺點有更多的認知。

參考

決策樹3:基尼指數–Gini index(CART)
西瓜書中的決策樹算法實現(ID3)

總結

以上是生活随笔為你收集整理的决策树挑选西瓜的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。