數據挖掘——幾個算法的python實現

這個學期開了“數據挖掘”課程,學習了幾個相關的算法,本着記錄和驗證一下學習成果的目的寫了這篇文章~

所謂是自己懂不算懂,能講清楚讓別人也懂纔算懂~

本文一共有四個算法的基本概念和實現,分別是KNN、ID3、C4.5、Apriori


目錄

1. KNN算法

1.1 算法介紹

1.2 算法實現步驟

1.3 算法的關鍵

1.4 Python具體實現(電影分類)

2. ID3算法

2.1 算法介紹

2.2 算法實現步驟

2.3 Python具體實現(打壘球活動)

3. C4.5算法

3.1 算法介紹

3.2 算法實現步驟

3.3 Python具體實現

4. Apriori算法

4.1 算法介紹

4.2 算法實現步驟

4.3 Python具體實現


1. KNN算法

1.1 算法介紹

KNN是一種監督學習算法,算法思想是通過計算新數據與訓練數據特徵值之間的距離,然後選取K個距離最近的鄰居進行分類判別。若K=1,新數據會被簡單分配給其近鄰的類。

 

1.2 算法實現步驟

① 選擇一種距離計算方法,計算新數據的某個特徵值與已知類別數據集中的數據點的相對應特徵值的距離;

② 按照距離大小進行升序排序,選取與新數據距離最小的K個點;

③ 對於離散分類,返回K個點中出現頻率最多的類作爲預測分類。

 

1.3 算法的關鍵

① 數據的所有特徵值都要是可比較的量化值。

② 樣本特徵要做歸一化處理。

③ 需要一個距離函數以計算兩個樣本之間的距離。

④ 確定K的值:K太大引起欠擬合、太小容易過擬合。交叉驗證以確定K的值。

 

1.4 Python具體實現(電影分類)

完整代碼和對代碼的解釋如下:

import numpy as np
import operator


# KNN算法分類愛情片和動作片
# 通過打鬥鏡頭次數和接吻鏡頭次數區分


# 模擬數據 打鬥次數 接吻次數
def init_data():
    data_X = np.array([[1,100],[2,96],[3,93],[6,90],[80,3],[93,2],[86,5]])
    data_Y = ['愛情片','愛情片','愛情片','愛情片','動作片','動作片','動作片']
    return data_X,data_Y


# data: 測試數據
# testData: 數據集合
# output:測試數據輸出
# k:取最接近數據的前幾個
def kNN(data, testData, output, k):
    # 獲取測試數據數量
    dataInputRow = testData.shape[0]
    # np.tile 數組沿各個方向複製
    # 這裏將輸入數據copy和測試數據數量保持一致,用來計算和測試數據的歐式距離
    # 數組排序效果如下所示,設所要分類的爲[2,93],訓練數據爲[3,93],[6,90],[80,3],[93,2]
    # [2,93][3,93]
    # [2,93][6,90]
    # [2,93][80,3]
    # [2,93][93,2]
    #計算與訓練數據之間的歐氏距離
    reduceData = np.tile(data, (dataInputRow,1)) - testData
    squareData = reduceData ** 2
    squareDataSum = squareData.sum(axis = 1)
    distance = squareDataSum ** .5
    # argsort()爲排序函數,得到的數組爲排序完成之後的索引數組
    # 如要排序的數組爲[-2,3,1]
    # argsort()排序之後得到的索引數組爲[0,2,1]
    sortDistance = distance.argsort()
    dataCount = {}
    # 統計排名靠前k數據的愛情片和動作片次數,取次數最高的做爲輸出
    for i in range(k):
        output_ = output[sortDistance[i]]
        dataCount[output_] = dataCount.get(output_,0) + 1
        # operator.itemgetteer(1)按值從小到大排序
        sortDataCount = sorted(dataCount.items(), key = operator.itemgetter(1), reverse = True)
    return sortDataCount[0][0]


if __name__ == '__main__':
    data_X,data_Y = init_data()
    print(kNN([2,93], data_X, data_Y, 3))

#By MIC_H

參考博客見:https://blog.csdn.net/u010479989/article/details/80606055


2. ID3算法

2.1 算法介紹

◆ ID3算法的結果是生成一個決策樹,決策樹中的每一個非葉結點對應着一個非類別屬性,樹枝代表這個屬性的值。一個葉結點代表從樹根到葉結點之間的路徑對應的記錄所屬的類別屬性值。

◆ 每一個非葉結點都將與屬性中具有最大信息量的非類別屬性相關聯。

◆ 採用信息增益來選擇出能夠最好地將樣本分類的屬性。

下面的一些解釋會用到這個表格,各屬性值的不同會如何影響活動的進行與否作爲例子

天氣 

溫度 

溼度 

風速 

活動 

炎熱

取消

炎熱

取消

炎熱

進行

適中

進行

寒冷

正常

進行

寒冷

正常

取消

寒冷

正常

進行

適中

取消

寒冷

正常

進行

適中

正常

進行

適中

正常

進行

適中

進行

炎熱

正常

進行

適中

取消

相關名詞

① 熵

通常熵表示事物的混亂程度,熵越大表示混亂程度越大,越小表示混亂程度越小。對於隨機事件S,如果我們知道它有N種取值情況,每種情況發生的概論爲,那麼這件事的熵就定義爲:

熵是對事件對應的屬性的不確定性的度量。一個屬性的熵越大,它蘊含的不確定信息越大,越有利於數據的分類。

例如對於打壘球的例子,要求活動的熵H(活動)。在活動一欄屬性中發現活動的取值有兩種:取消(5個)和進行(9個),它們所佔的比例分別爲5/14,9/14。那麼H(活動)的取值爲:,算出的結果約爲0.94。

② 信息增益

信息增益就是兩個熵的差,當差值越大說明按照此劃分對於事件的混亂程度減少越有幫助。

用上面的例子,則有

在天氣爲晴時有5種情況,發現活動取消有3種,進行有2種,計算現在的條件熵

=0.971

同理天氣爲陰時有4種情況,活動進行的有4種,則條件熵爲:

=0

同理天氣爲雨時有5種情況,活動取消的有2種,進行的有3種,則條件熵爲:

=0.971

由於按照天氣屬性不同取值劃分時,天氣爲晴佔整個情況的5/14,天氣爲陰占整個情況的4/14,天氣爲雨佔整個情況的5/14,

則按照天氣屬性不同取值劃分時的帶權平均值熵爲:算出的結果約爲0.693.

則此時的信息增益:

Gain(活動,天氣)= H(活動) - H(活動|天氣) = 0.94- 0.693 = 0.246

同理我們可以計算出按照溫度屬性不同取值劃分後的信息增益:

Gain(活動,溫度)= H(活動) - H(活動|溫度) = 0.94- 0.911 = 0.029

按照溼度屬性不同取值劃分後的信息增益:

Gain(活動,溼度)= H(活動) - H(活動|溼度) = 0.94- 0.789 = 0.151

按照風速屬性不同取值劃分後的信息增益:

Gain(活動,風速)= H(活動) - H(活動|風速) = 0.94- 0.892 = 0.048

 

2.2 算法實現步驟

① 計算各個屬性的熵;

② 計算信息增益;

③ 根據信息增益來構建決策樹。

用上面這個例子來走一下這個算法:

決策樹的構造就是要選擇當前信息增益最大的屬性作爲當前決策樹的節點,對於本例子來說,根據上面的計算結果,也就是天氣這個屬性,因此我們用天氣屬性作爲決策樹的根節點。

天氣屬性有3中取值:晴、陰、雨。

當天氣爲陰時,活動全爲進行,因此這個節點的結果是確定的;而當天氣爲晴或雨時,活動中有進行的也有取消的,結果是不確定的,所以還要用剩餘的其他屬性來劃分,也就是再次計算熵和信息增益,選擇最大信息增益的屬性來作爲下一個節點,直到結果是唯一的爲止。可見,這是一個遞歸建樹的過程。

最後的樹就是下面這樣的:

  

2.3 Python具體實現(打壘球活動)

完整代碼和對代碼的解釋如下:

from math import log
from operator import *


def storeTree(inputTree, filename):
    import pickle
    fw = open(filename, 'wb')  # pickle默認方式是二進制,需要制定'wb'
    pickle.dump(inputTree, fw)
    fw.close()


def grabTree(filename):
    import pickle
    fr = open(filename, 'rb')  # 需要制定'rb',以byte形式讀取
    return pickle.load(fr)


def createDataSet():
    '''
    dataSet=[[1,1,'yes'],[1,1,'yes'],[1,0,'no'],[0,1,'no'],[0,1,'no']]
    labels = ['no surfacing','flippers']
    '''
    dataSet = [['sunny', 'hot', 'high', 'weak', 'no'],
               ['sunny', 'hot', 'high', 'strong', 'no'],
               ['overcast', 'hot', 'high', 'weak', 'yes'],
               ['rain', 'mild', 'high', 'weak', 'yes'],
               ['rain', 'cool', 'normal', 'weak', 'yes'],
               ['rain', 'cool', 'normal', 'strong', 'no'],
               ['overcast', 'cool', 'normal', 'strong', 'yes'],
               ['sunny', 'mild', 'high', 'weak', 'no'],
               ['sunny', 'cool', 'normal', 'weak', 'yes'],
               ['rain', 'mild', 'normal', 'weak', 'yes'],
               ['sunny', 'mild', 'normal', 'strong', 'yes'],
               ['overcast', 'mild', 'high', 'strong', 'yes'],
               ['overcast', 'hot', 'normal', 'weak', 'yes'],
               ['rain', 'mild', 'high', 'strong', 'no']]
    labels = ['outlook', 'temperature', 'humidity', 'wind']
    return dataSet, labels


def calcShannonEnt(dataSet):  # 計算香農熵
    numEntries = len(dataSet)

    labelCounts = {}
    for featVec in dataSet:
        currentLabel = featVec[-1]  # 取得最後一列數據,該屬性取值情況有多少個
        if currentLabel not in labelCounts.keys():
            labelCounts[currentLabel] = 0
        labelCounts[currentLabel] += 1 #計算yes的次數和no的次數

    # 計算熵
    shannonEnt = 0.0
    for key in labelCounts:
        prob = float(labelCounts[key]) / numEntries
        shannonEnt -= prob * log(prob, 2)

    return shannonEnt


# 定義按照某個特徵進行劃分的函數splitDataSet
# 輸入三個變量(待劃分的數據集,特徵,分類值)
# axis特徵值中0代表no surfacing,1代表flippers
# value分類值中0代表否,1代表是
def splitDataSet(dataSet, axis, value):
    retDataSet = []
    for featVec in dataSet:  # 取大列表中的每個小列表
        if featVec[axis] == value:
            reduceFeatVec = featVec[:axis]   #去掉axis特徵
            reduceFeatVec.extend(featVec[axis + 1:])
            retDataSet.append(reduceFeatVec)

    return retDataSet  # 返回不含劃分特徵的子集


def chooseBestFeatureToSplit(dataSet):
    numFeature = len(dataSet[0]) - 1
    baseEntropy = calcShannonEnt(dataSet)
    bestInforGain = 0
    bestFeature = -1

    for i in range(numFeature):
        featList = [number[i] for number in dataSet]  # 得到某個特徵下所有值(某列)
        uniquelVals = set(featList)  # set無重複的屬性特徵值,得到所有無重複的屬性取值

        # 計算每個屬性i的概論熵
        newEntropy = 0
        for value in uniquelVals:
            subDataSet = splitDataSet(dataSet, i, value)  # 得到i屬性下取i屬性爲value時的集合
            prob = len(subDataSet) / float(len(dataSet))  # 每個屬性取值爲value時所佔比重
            newEntropy += prob * calcShannonEnt(subDataSet)
        inforGain = baseEntropy - newEntropy  # 當前屬性i的信息增益

        if inforGain > bestInforGain:
            bestInforGain = inforGain
            bestFeature = i

    return bestFeature  # 返回最大信息增益屬性下標


# 遞歸創建樹,用於找出出現次數最多的分類名稱
def majorityCnt(classList):
    classCount = {}
    for vote in classList:  # 統計當前劃分下每中情況的個數
        if vote not in classCount.keys():
            classCount[vote] = 0
        classCount[vote] += 1
    sortedClassCount = sorted(classCount.items, key=operator.itemgetter(1), reversed=True)  # reversed=True表示由大到小排序
    # 對字典裏的元素按照value值由大到小排序

    return sortedClassCount[0][0]


def createTree(dataSet, labels):
    classList = [example[-1] for example in dataSet]  # 創建數組存放所有標籤值,取dataSet裏最後一列(結果)
    # 類別相同,停止劃分

    # 判斷classList裏是否全是一類,如果是一類,這兩個值一定是相同的
    # count() 方法用於統計某個元素在列表中出現的次數
    if classList.count(classList[-1]) == len(classList):
        return classList[-1]  # 當全是一類時停止分割
    # 長度爲1,返回出現次數最多的類別
    if len(classList[0]) == 1:  # 當沒有更多特徵時停止分割,即分到最後一個特徵也沒有把數據完全分開,就返回多數的那個結果
        return majorityCnt(classList)
    # 按照信息增益最高選取分類特徵屬性
    bestFeat = chooseBestFeatureToSplit(dataSet)  # 返回分類的特徵序號,按照最大熵原則進行分類
    bestFeatLable = labels[bestFeat]  # 該特徵的label, #存儲分類特徵的標籤

    myTree = {bestFeatLable: {}}  # 構建樹的字典
    del (labels[bestFeat])  # 從labels的list中刪除該label

    featValues = [example[bestFeat] for example in dataSet]
    uniqueVals = set(featValues)
    for value in uniqueVals:
        subLables = labels[:]  # 子集合 ,將labels賦給sublabels,此時的labels已經刪掉了用於分類的特徵的標籤
        # 構建數據的子集合,並進行遞歸
        myTree[bestFeatLable][value] = createTree(splitDataSet(dataSet, bestFeat, value), subLables)
    return myTree


if __name__ == "__main__":
    my_Data, labels = createDataSet()

    # print(calcShannonEnt(my_Data))
    Mytree = createTree(my_Data, labels)
    print(Mytree)

#By MIC_H

參考博客見:https://www.cnblogs.com/wang2825/articles/8728913.html


3. C4.5算法

3.1 算法介紹

C4.5是在ID3上發展而來的。從建樹的依據這個角度來看,ID3是用信息增益來選擇特徵建樹,而C4.5則是用信息增益比來選擇特徵進行建樹。

信息增益比的定義如下:

 

其中     

成爲屬性a的固有值。

由表達式可知,Gain(D,a)所表示的仍然是信息增益,與ID3算法中的Gain(D,a)並無差別,但重點在於IV(a)這一項:如果屬性a的可能取值數目越多(即V越大),則IV(a)的值通常會越大,那麼最終的Gain_ratio的值會相應減小,以此來解決ID3算法的不足。

 

3.2 算法實現步驟

①計算各個屬性的熵;

② 計算信息增益;

③ 計算信息增益比;

④ 根據信息增益來構建決策樹。

 

3.3 Python具體實現

(這個算法中的yes和no我也找不着是什麼意思了,根據前面兩個屬性,我們就當成是是否買基金吧!)

完整代碼和對代碼的解釋如下:

from math import log
import operator


def createDataSet():
    dataSet = [[1, 1, 'yes'],
               [1, 1, 'yes'],
               [1, 0, 'yes'],
               [0, 1, 'no'],
               [0, 1, 'no'],
               [0, 0, 'no'],
               [0, 0, 'yes'],
               [0, 0, 'no']]
    labels = ['是否有房產', '是否有車']
    return dataSet, labels

#計算香農熵
def calcShannonEnt(dataSet):
    num = len(dataSet)    # 總共有多少行數據   8
    shannonEnt = 0.0  #初始化信息熵
    labelCounts = {}
    for item in dataSet:   # 遍歷整個數據集,每次取一行
        label = item[-1]  #取該行最後一列的值  即標籤
        if label not in labelCounts:  # 在字典中這個label是否存在
            labelCounts[label] = 0
        labelCounts[label] += 1    #{ 'yes':2,'no':3 }
    for key in labelCounts:
        p = float(labelCounts[key]/num)    # 即每個標籤所佔的比重
        #print( p )
        shannonEnt -= p * log(p,2)  #log base 2 計算信息熵
    return shannonEnt     # 返回根節點信息熵
    # labelCounts  {'yes': ..., 'no': ...}

def splitDataSet( dataSet,axis,value ):
    result = []
    for item in dataSet:     #  item:  [1,1,'yes']
        if item[axis] == value:
            r = item[:axis] + item[axis+1:]  # []+[1,'yes'] => r=> [1,'yes']
            # r = item[-1:]  #TODO: 只作熵運算的話,只用保留最後一列的值即可
            result.append(r)
    return result

# 選取當前數據集下,用於劃分數據集的最優特徵
def chooseBestFeatures(dataSet):
    # 1. 特徵總數  2
    numFeatures = len(dataSet[0]) -1  # 獲取當前數據集的特徵個數,最後一列是分類標籤    2
    # 2. 計算信息熵   1
    ent = calcShannonEnt(dataSet) # 計算當前數據集的信息熵    (根節點信息熵)
    # 3. 最佳信息熵
    bestGain = 0.0;
    # 4. 最佳特徵號 (0,1)
    bestFeatureID = -1 # 初始化最優信息增益和最優的特徵
    # 5.最佳信息增益率
    infogain = 0.0;
    # 循環特徵(列)
    for i in range(numFeatures):
        list1 = [line[i] for line in dataSet] # 獲取數據集中當前特徵下的所有值
        uniqueValues = set(list1)  # 每一列去重後的值, set可以去重 set(list) 將list列表強制轉換爲set  {0,1}
        #print( uniqueValues )
        newEnt = 0.0   # 條件熵
        s = 0.0
        for value in uniqueValues:
            # 調用splitDataSet ( 列,set中的值 ) -> 得到子集
            subDataSet = splitDataSet(dataSet,i,value)      # (dataSet,0,0) (dataSet,0,1) (dataSet,1,0) (dataSet,1,1)
            # 計算概率
            prob = len(subDataSet)/float(len(dataSet))      #       5/8          3/8           4/8           4/8
            # 計算熵
            newEnt += prob * calcShannonEnt(subDataSet)
            s -= prob * log(prob, 2);
        print( '信息熵爲:' + str(ent) )
        print( '第' + str(i) + '列的條件熵爲:' + str(newEnt) )
        gain = ent - newEnt
        print( '第' + str(i) + '列的信息增益爲:' + str(gain) )
        infogain = gain/s   # 求該列的信息增益比
        print( '第' + str(i) + '列的信息增益比爲:' + str(infogain) )
        if (infogain > bestGain):
            # 比較每個特徵的信息增益比,只要最好的信息增益比
            bestGain = infogain
            bestFeatureID = i
    return bestFeatureID

def classNum( classList ):
    '''
    classList: 分類的名稱
    '''
    classCount = {}    # 存各種分類出現的頻率 : {'yes',1,'no',2}
    for label in classList:
        classCount[label] = classCount.get(label,0) + 1
    # 對字典進行排序
    sortedClassCount = sorted( classCount.items(),key=operator.itemgetter(1),reverse=True )
    print( sortedClassCount )
    return sortedClassCount[0][0]


# 生成決策樹的總方法
def createTree(dataSet, labels, depth=0, max_features=None, max_depth=None):
    # 求出 dataSet 中的樣本所屬的類別,即遞歸停止的條件一
    # 返回當前數據集下標籤所有的值
    classList = [example[-1] for example in dataSet]  # ['yes','yes','yes','no','no','no','yes','no']
    # 終止條件1: 可以加上判斷 這個classList是否純淨
    if classList.count(classList[0]) == len(classList):
        # 純淨的意思就是此數據中所有特徵都相等
        # 當整個dataSet中的類別完全相同時類別已經純淨了,則停止繼續劃分,直接返回該類標籤
        return classList[0]
        # 終止條件2: 列中的取值種類   <=max_features 時.   max_features 即劃分時考慮的最大特徵數  默認爲 None
    if max_features == None:
        max_features = 1
    if len(dataSet[0]) <= max_features:
        return classNum(classList)  # 返回數量多的那一個 標籤

    #  max_depth  樹的最大深度,也就是說當樹的深度到達max_depth的時候無論還有多少可以分支的特徵,決策樹都會停止運算

    if max_depth != None:
        if depth >= max_depth:
            return classNum(classList)
        depth = depth + 1

    # 獲取最好的分類特徵索引
    # dataSet = [1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'yes'], [0, 1, 'no'], [0, 1, 'no'], [0, 0, 'no'], [0, 0, 'yes'], [0, 0, 'no']
    bestFeat = chooseBestFeatures(dataSet)  # bestFeat: 0      bestFeat: 0
    # print( 'bestFeat:',bestFeat )

    # 獲取該特徵的名字
    # labels = ['是否有房產', '是否有車']
    bestFeatLabel = labels[bestFeat]
    # print( 'bestFeatLabel:',bestFeatLabel )   # bestFeatLabel: 是否有房產    bestFeatLabel: 是否有車

    # 這裏直接使用字典變量來存儲樹信息,這對於回執樹形圖很重要
    myTree = {bestFeatLabel: {}}  # 當前數據集選取最好的特徵存儲在bestFeat中
    del (labels[bestFeat])  # 刪除已經在選取的特徵  此時  labels = ['是否有車']

    # 取出最優列的值
    featValues = [example[bestFeat] for example in dataSet]
    # featValues: [1, 1, 1, 0, 0, 0, 0, 0]      featValues: [1, 1, 0, 0, 0]
    # print( 'featValues:',featValues )
    # 去重
    uniqueVals = set(featValues)  # [1,0]
    # 根據這個列的這個 uniqueVals值來切分樹的節點
    for value in uniqueVals:
        #  myTree  -> 房產 -> 1
        #             房產 -> 0
        subLabels = labels[:]
        # print( 'subLabels:',subLabels )
        temp = splitDataSet(dataSet, bestFeat, value)
        # print( 'temp:',temp )
        myTree[bestFeatLabel][value] = createTree(temp, subLabels, depth=depth, max_features=max_features,
                                                  max_depth=max_depth)
    return myTree


if __name__ == '__main__':
    dataSet, labels = createDataSet()
    print(createTree(dataSet, labels))

#By MIC_H

參考博客見:https://blog.csdn.net/LA401088242/article/details/89160796


4. Apriori算法

4.1 算法介紹

Apriori算法是第一個關聯規則挖掘算法,也是最經典的算法。

它利用逐層搜索的迭代方法找出數據庫中項集的關係,以形成規則,其過程由連接(類矩陣運算)與剪枝(去掉那些沒必要的中間結果)組成。該算法中項集的概念即爲項的集合。包含K個項的集合爲k項集。項集出現的頻率是包含項集的事務數,稱爲項集的頻率。如果某項集滿足最小支持度,則稱它爲頻繁項集。

 

4.2 算法實現步驟

① 找出所有的頻繁項集,這些項集出現的支持度要大於等於預定義的最小支持度;

② 然後由頻集產生強關聯規則,這些規則必須滿足最小支持度和最小可信度;

③ 使用第1步找到的頻集產生期望的規則,產生只包含集合的項的所有規則,其中每一條規則的右部只有一項。

 

4.3 Python具體實現

完整代碼和對代碼的解釋如下:

def load_data_set():
    data_set = [['e1', 'e2', 'e5'],
                ['e2', 'e4'],
                ['e2', 'e3'],
                ['e1', 'e2', 'e4'],
                ['e1', 'e3'],
                ['e2', 'e3'],
                ['e1', 'e3'],
                ['e1', 'e2', 'e3', 'e5'],
                ['e1', 'e2', 'e3']]
    return data_set

# 轉成Set,爲生成頻繁項目集時掃描數據庫時以提供issubset()功能
def Create_C1(data_set):
    '''
    參數:數據庫事務集
    '''
    C1 = set()
    for t in data_set:
        for item in t:
            item_set = frozenset([item])  # frozenset用於生成不可變集合
            # 爲生成頻繁項目集時掃描數據庫時以提供issubset()功能
            C1.add(item_set)
    return C1

# 先驗性
def is_apriori(Ck_item, Lk_sub_1):
    '''
    參數:候選頻繁k項集,頻繁k-1項集
    '''
    for item in Ck_item:
        sub_item = Ck_item - frozenset([item])
        if sub_item not in Lk_sub_1:
            return False
    return True

# 剪掉不滿先驗性質的候選K項集
def Create_Ck(Lk_sub_1, k):
    '''
    # 參數:頻繁k-1項集,當前要生成的候選頻繁幾項集
    ****************
    存在先驗性質:任一頻繁項的所有非空子集也必須是頻繁的,
    也就是說任何非頻繁的(k-1)項集都不是頻繁k項集的子集。
    因此,如果一個候選k項集Ck的(k-1)項子集不在Lk-1中,則該候選也不可能是頻繁的,
    從而可以從Ck中刪除,獲得壓縮後的Ck。
    下文代碼中的is_apriori函數用於判斷是否滿足先驗性質,
    create_Ck函數中包含剪枝步驟,即若不滿足先驗性質,剪枝
    '''
    Ck = set()
    len_Lk_sub_1 = len(Lk_sub_1)
    list_Lk_sub_1 = list(Lk_sub_1)
    for i in range(len_Lk_sub_1):  # i: [0, len_Lk_sub_1)
        for j in range(i + 1, len_Lk_sub_1):  # j: [i+1, len_Lk_sub_1)
            l1 = list(list_Lk_sub_1[i])
            l2 = list(list_Lk_sub_1[j])
            l1.sort()
            l2.sort()
            # 判斷l1的前k-1-1個元素與l2的前k-1-1個元素對應位是否全部相同
            # list[s:t]:截取s到t範圍的元素生成一個新list
            if l1[0:k - 2] == l2[0:k - 2]:
                Ck_item = list_Lk_sub_1[i] | list_Lk_sub_1[j]  # 合併
                if is_apriori(Ck_item, Lk_sub_1):
                    Ck.add(Ck_item)
    return Ck


def Generate_Lk_By_Ck(data_set, Ck, min_support, support_data):
    '''
    參數:數據庫事務集,候選頻繁k項集,最小支持度,項目集-支持度dic
    基於壓縮後的Ck,掃描所有事務,對Ck中的每個項進行計數,
    然後刪除不滿足最小支持度的項,從而獲得頻繁k項集
    '''
    Lk = set()
    # 通過dic記錄候選頻繁k項集的事務支持個數
    item_count = {}
    for t in data_set:
        for Ck_item in Ck:
            if Ck_item.issubset(t):
                if Ck_item not in item_count:
                    item_count[Ck_item] = 1
                else:
                    item_count[Ck_item] += 1
    data_num = float(len(data_set))
    for item in item_count:
        if (item_count[item] / data_num) >= min_support:
            Lk.add(item)
            support_data[item] = item_count[item] / data_num
    return Lk  # 篩選出並返回頻繁k項集

# 生成所有的頻繁項目集
def Generate_L(data_set, max_k, min_support):
    '''
    參數:數據庫事務集,求的最高項目集爲k項,最小支持度
    '''
    # 創建一個頻繁項目集爲key,其支持度爲value的dic
    support_data = {}
    C1 = Create_C1(data_set)
    L1 = Generate_Lk_By_Ck(data_set, C1, min_support, support_data)
    Lk_sub_1 = L1.copy()  # 對L1進行淺copy
    L = []
    L.append(Lk_sub_1)  # 末尾添加指定元素
    for k in range(2, max_k + 1):
        Ck = Create_Ck(Lk_sub_1, k) #根據先驗性剪掉一部分
        Lk = Generate_Lk_By_Ck(data_set, Ck, min_support, support_data) #得出頻繁K項集
        Lk_sub_1 = Lk.copy()
        L.append(Lk_sub_1)
    return L, support_data

# 生成關聯規則
def Generate_Rule(L, support_data, min_confidence):
    '''
    參數:所有的頻繁項目集,項目集-支持度dic,最小置信度
    '''
    rule_list = []
    sub_set_list = []
    for i in range(len(L)):
        for frequent_set in L[i]:
            for sub_set in sub_set_list:
                if sub_set.issubset(frequent_set):
                    # 計算置信度
                    conf = support_data[frequent_set] / support_data[sub_set]
                    # 將rule聲明爲tuple
                    rule = (sub_set, frequent_set - sub_set, conf)
                    # 篩選置信度大於等於最小置信度的規則
                    if conf >= min_confidence and rule not in rule_list:
                        rule_list.append(rule)
            sub_set_list.append(frequent_set)
    return rule_list


if __name__ == "__main__":
    data_set = load_data_set()
    '''
    print("Test")
    # 數據庫事務打印
    for t in data_set:
        print(t)
    '''
    '''
    print("Test")
    # 候選頻繁1項集打印
    C1 = Create_C1(data_set)
    for item in C1:
        print(item)
    '''
    '''
    # 頻繁1項集打印
    print("Test")
    L = Generate_L(data_set, 1, 0.2)
    for item in L:
        print(item)
    '''
    '''
    # 頻繁k項集打印
    print("Test")
    L, support_data = Generate_L(data_set, 2, 0.2)
    for item in L:
        print(item)
    '''
    '''
    # 關聯規則測試
    print("Test")
    L, support_data = Generate_L(data_set, 3, 0.2)
    rule_list = Generate_Rule(L, support_data, 0.7)
    for item in support_data:
        print(item, ": ", support_data[item])
    print("-----------------------")
    for item in rule_list:
        print(item[0], "=>", item[1], "'s conf:", item[2])
    '''

    L, support_data = Generate_L(data_set, 3, 0.2)
    rule_list = Generate_Rule(L, support_data, 0.7)
    for Lk in L:
        print("=" * 55)
        print("frequent " + str(len(list(Lk)[0])) + "-itemsets\t\tsupport")
        print("=" * 55)
        for frequent_set in Lk:
            print(frequent_set, support_data[frequent_set])
    print()
    print("Rules")
    for item in rule_list:
        print(item[0], "=>", item[1], "'s conf: ", item[2])

# By MIC_H

最後出來的結果如下圖所示:

參考博客見:https://blog.csdn.net/yo_bc/article/details/80379399#commentBox

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章