D3算法編寫決策樹

 前言

所謂構建決策樹,node

就是遞歸的對數據集參數進行「最優特徵」的選擇。而後按最優特徵分類成各個子數據集,繼續遞歸。python

最優特徵的選擇:依次計算按照各個特徵進行分類之後數據集的熵,各個子數據集的熵比較後,其中擁有最小的熵的數據集就是最優的分類結果,這次分類的特徵就是最優特徵。算法

熵的計算:熵計算的是數據集的純淨程度,數據集的熵的大小隻和數據集中各數據樣本的最終分類結果的分佈有關。假設數據集中全部數據都是「同一種類」的數據,那麼其熵就是0,表示是最純淨的數據。數組

(因此最優特徵的選擇就變成了,先計算分類前的數據集的熵,再計算按某種特徵分類後其各個子數據集的熵的指望,而後取原數據集熵與分類後的熵指望之差做爲評價分類效果的標準。)app

(此處之因此計算分類後全部子數據集的熵的指望其實很好理解。指望也能夠當作一堆離散數據的平均值,假設咱們把原數據集分紅了三堆子數據集,這三堆子數據集哪一堆也不能表明分類後的結果,只有三堆總結果再求個指望也就是三堆的平均值,才能表明分類後的結果)函數

 

 

熵的計算

咱們用數據集中各結果分類出現的機率來做爲計算熵的決定因素。測試

假設整個樣本數據集中樣本都有着「統一的分類結果」時,出現該分類結果的機率是100%,其熵就是0。spa

而若是數據集中有10個樣本,每一個樣本都有一個獨立的結果分類,那麼出現每一種結果的機率都是10%,這種結果的不肯定性天然要比上一個大(上一個但是100%的肯定結果),其最終熵值確定也要更大。3d

 

至此咱們能夠看出,咱們須要找到一個函數能把事件出現的機率映射成熵值。code

這個函數就是如下這個函數

 

其函數圖像爲

 

x軸是事件出現的機率

y軸是熵的值

 

經過圖像咱們能夠看出隨着事件出現的機率愈來愈大,熵也愈來愈小,直至到0。

 

假設數據集中一共有出現三種分類結果的可能,該數據集的熵天然是算出這三種分類結果的熵後在求其三者的指望,使用指望做爲該數據集的熵。

 

python代碼實現以下:

from math import log

"""
數據集,
二維數組中的前三個元素都是特徵,
最後一個元素是樣本的分類結果。
"""
data = [["有眼鏡", "短髮", "", ""],
        ["有眼鏡", "長髮", "", ""],
        ["有眼鏡", "短髮", "", ""],
        ["沒眼鏡", "長髮", "", ""],
        ["沒眼鏡", "短髮", "", ""]]

"""
data:須要計算熵的數據集
return:該數據集的熵

計算數據集的熵
"""
def calcShannon(data):
    #
    shannonMean = 0
    # 數據總量
    sumDataNum = len(data)
    # 數據集的全部分類狀況
    classify = [man[-1] for man in data]
    # 循環每一種分類結果,計算該分類結果的熵,並求指望
    for resClassify in set(classify):
        # 該分類結果的「發生」機率
        p = classify.count(resClassify) / sumDataNum
        # 計算該分類結果的熵
        shannon = -log(p, 2)
        # 求指望
        shannonMean += p * shannon
    return shannonMean

print(calcShannon(data))

 

構建決策樹

咱們的最終目的是要構建出下圖結構的決策樹。

 

 

上圖轉換成python字典數據格式爲:

{特徵1:{值1:yes,值2:{特徵2:{值1:no,值2:yes}}}}

 

完整python代碼以下:

from math import log

"""
數據集,
二維數組中的前三個元素都是特徵,
最後一個元素是樣本的分類結果。
"""
data = [["有眼鏡", "短髮", "", ""],
        ["有眼鏡", "長髮", "", ""],
        ["有眼鏡", "短髮", "", ""],
        ["沒眼鏡", "長髮", "", ""],
        ["沒眼鏡", "短髮", "", ""],
        ["有眼鏡", "長髮", "", ""],
        ["有眼鏡", "長髮", "", ""]]

"""
樣本參數中各個特徵的描述信息
"""
labels = ["是否戴眼鏡", "頭髮長短", "身材"]

"""
計算數據集的熵

data:須要計算熵的數據集

return:該數據集的熵
"""
def calcShannon(data):
    #
    shannonMean = 0
    # 數據總量
    sumDataNum = len(data)
    # 數據集的全部分類狀況
    classify = [man[-1] for man in data]
    # 循環每一種分類結果,計算該分類結果的熵,並求指望
    for resClassify in set(classify):
        # 該分類結果的「發生」機率
        p = classify.count(resClassify) / sumDataNum
        # 計算該分類結果的熵
        shannon = -log(p, 2)
        # 求指望
        shannonMean += p * shannon
    return shannonMean


"""
統計分類數組中出現最多的項,並返回該項的值
"""
def statisticsMostClassify(classify):
    map = {}
    for resClassify in classify:
        value = map.get(resClassify)
        if value:
            map[resClassify] = value + 1
        else:
            map[resClassify] = 1
    mostClassify = sorted(map.items(), key=lambda item: item[1])
    return mostClassify[-1][0]


"""
對數據進行分類,並返回分類後的結果(如按照a特徵分類後,分類後的結果數據集中就沒有a特徵值了)

index:按照第幾個特徵開始分類,從0開始
value:按照該特徵的什麼值進行分類
data:待分類數據
"""
def categorizationOfData(index, value, data):
    resData = []
    # 循環每個樣本,若是第index個特徵的值符合指定特徵,就把該特徵刪除後保存
    for man in data:
        if man[index] == value:
            tmpMan = man[:]
            tmpMan.pop(index)
            resData.append(tmpMan)
    return resData


"""
D3算法構建決策樹
步驟:
0、獲取數據集
一、判斷當前數據集是否須要繼續分類,如不須要,則返回結果分類
二、找到最優特徵
三、根據最優特徵進行分類,並把最優特徵刪去
四、
"""
def createTree(data, labels):
    classify = [man[-1] for man in data]
    # 若是當前數據集數據都是同一分類結果則不用繼續分類
    if len(set(classify)) == 1:
        return classify[0]

    # 若是數據集中的樣本沒有特徵了,則返回數據集中出現最多的分類結果
    if not labels:
        return statisticsMostClassify(classify)

    # 原數據集香農熵
    originalShannon = calcShannon(data)
    # 熵差
    diffShannon = 0
    # 最優特徵(index下標)
    bestFeatureIndex = 0
    # 按照最優特徵分類後的結果{分類結果值:分類後的數據集}
    bestClassifyData = {}
    # 循環計算按照每個特徵分類後的結果,選擇其中使得熵差值最大的特徵做爲最優特徵
    for i in range(len(labels)):
        tmpClassifyData = {}
        # 取出第i個特徵的全部可能值
        valueAll = [man[i] for man in data]
        valueSetAll = set(valueAll)  # 去重後的全部可能特徵值
        # 循環計算按各個可能值分類後的熵,而後求指望
        classifyShannonMean = 0
        for value in valueSetAll:
            resData = categorizationOfData(i, value, data)  # 按該特徵值分類後結果
            classifyShannon = calcShannon(resData)  # 分類後的熵
            classifyShannonMean += (valueAll.count(value) / len(valueAll)) * classifyShannon  # 指望
            tmpClassifyData.update({value: resData})
        # 計算按照當前特徵分類後的熵差
        diff = originalShannon - classifyShannonMean
        if diff >= diffShannon:
            bestFeatureIndex = i
            bestClassifyData = tmpClassifyData
            diffShannon = diff
    # 按結果分類,把labels中的最優特徵刪去,構建節點,並將各結果數據集遞歸
    bestFeature = labels[bestFeatureIndex]  # 獲取最優特徵的中文描述
    tmpLabels = labels[:]
    tmpLabels.pop(bestFeatureIndex)
    node = {bestFeature: {}}  # 當前節點
    for key in bestClassifyData:
        resClassify = createTree(bestClassifyData[key], tmpLabels)
        node[bestFeature].update({key: resClassify})
    return node

# 使用樣本測試樣本構建決策樹
tree = createTree(data, labels)
# 打印樹
print(tree)
# 獲得結果:{'是否戴眼鏡': {'有眼鏡': {'身材': {'胖': {'頭髮長短': {'長髮': '男', '短髮': '女'}}, '瘦': '女'}}, '沒眼鏡': '男'}}

 

(ps:不知不覺已是第100篇隨筆了,仍是頗有成就感的^_^)

相關文章
相關標籤/搜索