1. 程式人生 > >機器學習演算法總結(三)

機器學習演算法總結(三)

1、決策樹

決策樹是通過一系列規則對資料進行分類的過程。它提供一種在什麼條件下會得到什麼值的類似規則的方法。決策樹分為分類樹和迴歸樹兩種,分類樹對離散變數做決策樹,迴歸樹對連續變數做決策樹。

1.2 決策樹的學習過程

一棵決策樹的生成過程主要分為以下3個部分:

  • 特徵選擇:特徵選擇是指從訓練資料中眾多的特徵中選擇一個特徵作為當前節點的分裂標準,如何選擇特徵有著很多不同量化評估標準標準,從而衍生出不同的決策樹演算法。
  • 決策樹生成: 根據選擇的特徵評估標準,從上至下遞迴地生成子節點,直到資料集不可分則停止決策樹停止生長。 樹結構來說,遞迴結構是最容易理解的方式。
  • 剪枝:決策樹容易過擬合,一般來需要剪枝,縮小樹結構規模、緩解過擬合。剪枝技術有預剪枝和後剪枝兩種。

1.3 基於資訊理論的三種決策樹演算法

劃分資料集的最大原則是:使無序的資料變的有序。如果一個訓練資料中有20個特徵,那麼選取哪個做劃分依據?這就必須採用量化的方法來判斷,量化劃分方法有多重,其中一項就是“資訊理論度量資訊分類”。基於資訊理論的決策樹演算法有ID3、CART和C4.5等演算法,其中C4.5和CART兩種演算法從ID3演算法中衍生而來。

  CART和C4.5支援資料特徵為連續分佈時的處理,主要通過使用二元切分來處理連續型變數,即求一個特定的值-分裂值:特徵值大於分裂值就走左子樹,或者就走右子樹。這個分裂值的選取的原則是使得劃分後的子樹中的“混亂程度”降低,具體到C4.5和CART演算法則有不同的定義方式。

  ID3演算法由Ross Quinlan發明,建立在“奧卡姆剃刀”的基礎上:越是小型的決策樹越優於大的決策樹(be simple簡單理論)。ID3演算法中根據資訊理論的資訊增益評估和選擇特徵,每次選擇資訊增益最大的特徵做判斷模組。ID3演算法可用於劃分標稱型資料集,沒有剪枝的過程,為了去除過度資料匹配的問題,可通過裁剪合併相鄰的無法產生大量資訊增益的葉子節點(例如設定資訊增益閥值)。使用資訊增益的話其實是有一個缺點,那就是它偏向於具有大量值的屬性–就是說在訓練集中,某個屬性所取的不同值的個數越多,那麼越有可能拿它來作為分裂屬性,而這樣做有時候是沒有意義的,另外ID3不能處理連續分佈的資料特徵,於是就有了C4.5演算法。CART演算法也支援連續分佈的資料特徵。

  C4.5是ID3的一個改進演算法,繼承了ID3演算法的優點。C4.5演算法用資訊增益率來選擇屬性,克服了用資訊增益選擇屬性時偏向選擇取值多的屬性的不足在樹構造過程中進行剪枝;能夠完成對連續屬性的離散化處理;能夠對不完整資料進行處理。C4.5演算法產生的分類規則易於理解、準確率較高;但效率低,因樹構造過程中,需要對資料集進行多次的順序掃描和排序。也是因為必須多次資料集掃描,C4.5只適合於能夠駐留於記憶體的資料集。

  CART演算法的全稱是Classification And Regression Tree,採用的是Gini指數(選Gini指數最小的特徵s)作為分裂標準,同時它也是包含後剪枝操作。ID3演算法和C4.5演算法雖然在對訓練樣本集的學習中可以儘可能多地挖掘資訊,但其生成的決策樹分支較大,規模較大。為了簡化決策樹的規模,提高生成決策樹的效率,就出現了根據GINI係數來選擇測試屬性的決策樹演算法CART。

1.4 構造一顆決策樹

決策樹歸納演算法(ID3)
在決策樹演算法中,比較重要的一點是我們如何確定哪個屬性應該先選擇出來,哪個屬性應該後選擇出來當做樹的節點。這裡就涉及到了一個新的概念,叫做資訊獲取量,公式如下:
Gain(A)=Info(D)-Info_A(D) —->A屬性的資訊獲取量的值就等於,不按任何屬性進行分類的時候的資訊量加上有按A這個屬性進行分類的時候的資訊量(注意這裡資訊量的符號是負號,所以說“加上”)。
以下是否購買電腦的案例為例子,給出了14個例項,如下圖所示:
這裡寫圖片描述
不按任何屬性進行分類的情況下,計算資訊獲取量Info(D):
這裡寫圖片描述
以年齡屬性進行分類的情況下,計算資訊獲取量:
這裡寫圖片描述
所以,Gain(age)=0.940-0.694=0.246 bits
同理,我們可以算出Gain(income) = 0.029, Gain(student) = 0.151, Gain(credit_rating)=0.048。比較大小,年齡的資訊獲取量是最大的,所以選擇年齡作為第一個根節點。再次同理,後面的節點選擇也是按照這樣的計算方法來決定以哪個屬性作為節點。

結束條件
當我們使用遞迴的方法來建立決策樹時,什麼時候停止節點的建立很關鍵。綜上,停止節點建立的條件有以下幾點:
a、給定節點的所有樣本屬性都屬於同一種標記的時候,比如(2)中的例子,以年齡為屬性建立的節點下,有三個分支senior,youth、middle_age。其中middle_age的所有例項的標記都是yes,也就是說中年人都會買電腦,這種情況下,這個節點就可以設定成樹葉節點了。
b、當沒有剩餘屬性用來進一步劃分樣本時,就停止節點的建立,採用多數表決。
c、分枝

1.5 減枝(避免overfitting)
當樹的深度太大時,設計的演算法在訓練集上的表現會比較好,但是在測試集上的表現卻會很一般,這時我們就要對樹進行一定的裁剪:
(1)先剪枝
當分到一定程度,就不向下增長樹了。
(2)後剪枝

1.6 決策樹優缺點
決策樹的優點: 直觀,便於理解,小規模資料集有效
決策樹的缺點:處理連續變數不好;類別較多時,錯誤增加的比較快;可規模性一般。

決策樹模型還有一個很大的優勢,就是可以容忍缺失資料。如果決策樹中某個條件缺失,可以按一定的權重分配繼續往以後的分支走,最終的結果可能有多個,每個結果又一定的概率,即:
最終結果=某個分支的結果 x 該分支的權重(該分支下的結果數/總結果數)

決策樹主要解決分類問題(結果是離散資料),如果結果是數字,不會考慮這樣的事實:有些數字相差很近,有些數字相差很遠。為了解決這個問題,可以用方差來代替熵或基尼不純度。

例項

#!/usr/bin/python
# -*-coding:utf-8-*-
# __author__ = 'ShenJun'

import pandas as pda
from sklearn.tree import DecisionTreeClassifier as DTC
from sklearn.tree import export_graphviz
from sklearn.externals.six import StringIO
import numpy as np
path="F:\\網路爬蟲\\視訊課程\\原始碼\原始碼\\lesson.csv"
fname=open(path,encoding="utf-8")
data=pda.read_csv(fname)
x=data.iloc[:,1:5].as_matrix()
y=data.iloc[:,5].as_matrix()
for i in range(0,len(x)):
    for j in range(0,len(x[i])):
        thisdata=x[i][j]
        if(thisdata=="是" or thisdata=="多" or thisdata=="高"):
            x[i][j]=int(1)
        else:
            x[i][j]=int(-1)
for i in range(0,len(y)):
    thisdata=y[i]
    if(thisdata=="高"):
        y[i]=1
    else:
        y[i]=-1
#轉化格式,將x,y轉化為資料框,然後再轉化為陣列並指定格式
x1=pda.DataFrame(x)
y1=pda.DataFrame(y)
x2=x1.as_matrix().astype(int)
y2=y1.as_matrix().astype(int)

#建立決策樹
# print(type(y2))
# print(type(x2))
dtc=DTC(criterion="entropy")
dtc.fit(x2,y2)
#直接預測銷量高低
x3=np.array([1,-1,-1,1],[1,1,1,1],[-1,1,-1,1])
rst=dtc.predict(x3)
print(rst)

#視覺化決策樹,要通過安裝export_graphviz軟體進行開啟
with open("J:\\Program\\Python\\Python爬蟲\\資料分析與資料探勘\\dtc.dot","w") as file:
    export_graphviz(dtc,feature_names=["combat","num","promotion","datum"],out_file=file)

2、Kmeans聚類演算法

2.1 Kmeans演算法流程
輸入:聚類個數k,資料集Xmxn。
輸出:滿足方差最小標準的k個聚類。

(1) 選擇k個初始中心點,例如c[0]=X[0] , … , c[k-1]=X[k-1];

(2) 對於X[0]….X[n],分別與c[0]…c[k-1]比較,假定與c[i]差值最少,就標記為i;

(3) 對於所有標記為i點,重新計算c[i]={ 所有標記為i的樣本的每個特徵的均值};

(4) 重複(2)(3),直到所有c[i]值的變化小於給定閾值或者達到最大迭代次數。

Kmeans的時間複雜度:O(tkmn),空間複雜度:O((m+k)n)。其中,t為迭代次數,k為簇的數目,m為樣本數,n為特徵數。

2.2 Kmeans演算法優缺點

優點
(1). 演算法原理簡單。需要調節的超引數就是一個k。

(2). 由具有出色的速度和良好的可擴充套件性。

缺點
(1). 在 Kmeans 演算法中 k 需要事先確定,這個 k 值的選定有時候是比較難確定。

(2). 在 Kmeans 演算法中,首先需要初始k個聚類中心,然後以此來確定一個初始劃分,然後對初始劃分進行優化。這個初始聚類中心的選擇對聚類結果有較大的影響,一旦初始值選擇的不好,可能無法得到有效的聚類結果。多設定一些不同的初值,對比最後的運算結果,一直到結果趨於穩定結束。

(3). 該演算法需要不斷地進行樣本分類調整,不斷地計算調整後的新的聚類中心,因此當資料量非常大時,演算法的時間開銷是非常大的。

(4). 對離群點很敏感。

(5). 從資料表示角度來說,在 Kmeans 中,我們用單個點來對 cluster 進行建模,這實際上是一種最簡化的資料建模形式。這種用點來對 cluster 進行建模實際上就已經假設了各 cluster的資料是呈圓形(或者高維球形)或者方形等分佈的。不能發現非凸形狀的簇。但在實際生活中,很少能有這種情況。所以在 GMM 中,使用了一種更加一般的資料表示,也就是高斯分佈。

(6). 從資料先驗的角度來說,在 Kmeans 中,我們假設各個 cluster 的先驗概率是一樣的,但是各個 cluster 的資料量可能是不均勻的。舉個例子,cluster A 中包含了10000個樣本,cluster B 中只包含了100個。那麼對於一個新的樣本,在不考慮其與A cluster、 B cluster 相似度的情況,其屬於 cluster A 的概率肯定是要大於 cluster B的。

(7). 在 Kmeans 中,通常採用歐氏距離來衡量樣本與各個 cluster 的相似度。這種距離實際上假設了資料的各個維度對於相似度的衡量作用是一樣的。但在 GMM 中,相似度的衡量使用的是後驗概率 αcG(x|μc,∑c) ,通過引入協方差矩陣,我們就可以對各維度資料的不同重要性進行建模。

(8). 在 Kmeans 中,各個樣本點只屬於與其相似度最高的那個 cluster ,這實際上是一種 hard clustering 。

2.3 K值的確定

2.3.1 根據問題內容確定

2.3.2 肘部法則
如果問題中沒有指定k的值,可以通過肘部法則這一技術來估計聚類數量。肘部法則會把不同k值的成本函式值畫出來。隨著k值的增大,平均畸變程度會減小;每個類包含的樣本數會減少,於是樣本離其重心會更近。但是,隨著k值繼續增大,平均畸變程度的改善效果會不斷減低。k值增大過程中,畸變程度的改善效果下降幅度最大的位置對應的k值就是肘部。為了讓讀者看的更加明白,下面讓我們通過一張圖用肘部法則來確定最佳的k值。下圖資料明顯可分成兩類:
這裡寫圖片描述
從圖中可以看出,k值從1到2時,平均畸變程度變化最大。超過2以後,平均畸變程度變化顯著降低。因此最佳的k是2。

2.3.3 與層次聚類結合
經常會產生較好的聚類結果的一個有趣策略是,首先採用層次凝聚演算法決定結果粗的數目,並找到一個初始聚類,然後用迭代重定位來改進該聚類。

2.3.4 穩定性方法
穩定性方法對一個數據集進行2次重取樣產生2個數據子集,再用相同的聚類演算法對2個數據子集進行聚類,產生2個具有k個聚類的聚類結果,計算2個聚類結果的相似度的分佈情況。2個聚類結果具有高的相似度說明k個聚類反映了穩定的聚類結構,其相似度可以用來估計聚類個數。採用次方法試探多個k,找到合適的k值。

2.3.5 使用canopy演算法進行初始劃分
基於Canopy Method的聚類演算法將聚類過程分為兩個階段

(1) 聚類最耗費計算的地方是計算物件相似性的時候,Canopy Method在第一階段選擇簡單、計算代價較低的方法計算物件相似性,將相似的物件放在一個子集中,這個子集被叫做Canopy,通過一系列計算得到若干Canopy,Canopy之間可以是重疊的,但不會存在某個物件不屬於任何Canopy的情況,可以把這一階段看做資料預處理;

(2) 在各個Canopy內使用傳統的聚類方法(如Kmeans),不屬於同一Canopy的物件之間不進行相似性計算。

從這個方法起碼可以看出兩點好處:首先,Canopy不要太大且Canopy之間重疊的不要太多的話會大大減少後續需要計算相似性的物件的個數;其次,類似於Kmeans這樣的聚類方法是需要人為指出K的值的,通過(1)得到的Canopy個數完全可以作為這個k值,一定程度上減少了選擇k的盲目性。

2.4 初始質心的選取
選擇適當的初始質心是基本kmeans演算法的關鍵步驟。常見的方法是隨機的選取初始中心,但是這樣簇的質量常常很差。處理選取初始質心問題的一種常用技術是:多次執行,每次使用一組不同的隨機初始質心,然後選取具有最小SSE(誤差的平方和)的簇集。這種策略簡單,但是效果可能不好,這取決於資料集和尋找的簇的個數。

第二種有效的方法是,取一個樣本,並使用層次聚類技術對它聚類。從層次聚類中提取k個簇,並用這些簇的質心作為初始質心。該方法通常很有效,但僅對下列情況有效:(1)樣本相對較小,例如數百到數千(層次聚類開銷較大);(2) k相對於樣本大小較小。

第三種選擇初始質心的方法,隨機地選擇第一個點,或取所有點的質心作為第一個點。然後,對於每個後繼初始質心,選擇離已經選取過的初始質心最遠的點。使用這種方法,確保了選擇的初始質心不僅是隨機的,而且是散開的。但是,這種方法可能選中離群點。此外,求離當前初始質心集最遠的點開銷也非常大。為了克服這個問題,通常該方法用於點樣本。由於離群點很少(多了就不是離群點了),它們多半不會在隨機樣本中出現。計算量也大幅減少。

第四種方法就是上面提到的canopy演算法。

2.5 距離的度量

常用的距離度量方法包括:歐幾里得距離和餘弦相似度。兩者都是評定個體間差異的大小的。

歐氏距離是最常見的距離度量,而餘弦相似度則是最常見的相似度度量,很多的距離度量和相似度度量都是基於這兩者的變形和衍生,所以下面重點比較下兩者在衡量個體差異時實現方式和應用環境上的區別。

藉助三維座標系來看下歐氏距離和餘弦相似度的區別:
這裡寫圖片描述

從圖上可以看出距離度量衡量的是空間各點間的絕對距離,跟各個點所在的位置座標(即個體特徵維度的數值)直接相關;而餘弦相似度衡量的是空間向量的夾角,更加的是體現在方向上的差異,而不是位置。如果保持A點的位置不變,B點朝原方向遠離座標軸原點,那麼這個時候餘弦相似cosθ是保持不變的,因為夾角不變,而A、B兩點的距離顯然在發生改變,這就是歐氏距離和餘弦相似度的不同之處。

根據歐氏距離和餘弦相似度各自的計算方式和衡量特徵,分別適用於不同的資料分析模型:歐氏距離能夠體現個體數值特徵的絕對差異,所以更多的用於需要從維度的數值大小中體現差異的分析,如使用使用者行為指標分析使用者價值的相似度或差異;而餘弦相似度更多的是從方向上區分差異,而對絕對的數值不敏感,更多的用於使用使用者對內容評分來區分使用者興趣的相似度和差異,同時修正了使用者間可能存在的度量標準不統一的問題(因為餘弦相似度對絕對數值不敏感)。

因為歐幾里得距離度量會受指標不同單位刻度的影響,所以一般需要先進行標準化,同時距離越大,個體間差異越大;空間向量餘弦夾角的相似度度量不會受指標刻度的影響,餘弦值落於區間[-1,1],值越大,差異越小。但是針對具體應用,什麼情況下使用歐氏距離,什麼情況下使用餘弦相似度?

從幾何意義上來說,n維向量空間的一條線段作為底邊和原點組成的三角形,其頂角大小是不確定的。也就是說對於兩條空間向量,即使兩點距離一定,他們的夾角餘弦值也可以隨意變化。感性的認識,當兩使用者評分趨勢一致時,但是評分值差距很大,餘弦相似度傾向給出更優解。舉個極端的例子,兩使用者只對兩件商品評分,向量分別為(3,3)和(5,5),這兩位使用者的認知其實是一樣的,但是歐式距離給出的解顯然沒有餘弦值合理。

2.6 聚類效果評估
本節為將介紹另一種聚類演算法效果評估方法稱為輪廓係數(Silhouette Coefficient)。輪廓係數是類的密集與分散程度的評價指標。它會隨著類的規模增大而增大。彼此相距很遠,本身很密集的類,其輪廓係數較大,彼此集中,本身很大的類,其輪廓係數較小。輪廓係數是通過所有樣本計算出來的,計算每個樣本分數的均值,計算公式如下:
這裡寫圖片描述
a是每一個類中樣本彼此距離的均值,b是一個類中樣本與其最近的那個類的所有樣本的距離的均值。

#!/usr/bin/python
# -*-coding:utf-8-*-
# __author__ = 'ShenJun'

import pandas as pda
import matplotlib.pylab as pyl
import numpy as np
from sklearn.cluster import Birch
from sklearn.cluster import KMeans
import pymysql
'''
path="F:\\網路爬蟲\\視訊課程\\原始碼\原始碼\\luqu.csv"
fname=open(path,encoding="utf-8")
data=pda.read_csv(fname)
x=data.iloc[:,1:4].as_matrix()
#n_clusters聚類數  n_jobs:指定執行緒數   max_iter:最大迭代數
kms=KMeans(n_clusters=4,max_iter=500)
y=kms.fit_predict(x)
# print(y)
# print(data)
#視覺化,x代表學生序號,y代表學生類別
s=np.arange(0,len(y))
pyl.plot(s,y,"o")
pyl.show()

'''
'''
聚類演算法實現商品的聚類
'''
conn=pymysql.connect(host="127.0.0.1",user="root",passwd="123456",db="taobao")
sql="select price,comment from taob limit 1000"
data=pda.read_sql(sql,conn)
# print(data)
x=data.iloc[:,:].as_matrix()
kms=KMeans(n_clusters=3)
y=kms.fit_predict(x)
# print(y)
for i in range(0,len(y)):
    if(y[i]==0):
        pyl.plot(data.iloc[i:i+1,0:1].as_matrix(),data.iloc[i:i+1,1:2].as_matrix(),"*r")
    if(y[i]==1):
        pyl.plot(data.iloc[i:i+1,0:1].as_matrix(),data.iloc[i:i+1,1:2].as_matrix(),"sy")
    if(y[i]==2):
        pyl.plot(data.iloc[i:i+1,0:1].as_matrix(),data.iloc[i:i+1,1:2].as_matrix(),"pk")
pyl.show()

3、Apriori演算法

3.1 Apriori演算法簡介
Apriori演算法的名字正是基於這樣的事實:演算法使用頻繁項集性質的先驗性質,即頻繁項集的所有非空子集也一定是頻繁的。Apriori演算法使用一種稱為逐層搜尋的迭代方法,其中k項集用於探索(k+1)項集。首先,通過掃描資料庫,累計每個項的計數,並收集滿足最小支援度的項,找出頻繁1項集的集合。該集合記為L1。然後,使用L1找出頻繁2項集的集合L2,使用L2找出L3,如此下去,直到不能再找到頻繁k項集。每找出一個Lk需要一次資料庫的完整掃描。Apriori演算法使用頻繁項集的先驗性質來壓縮搜尋空間。

3.2 基本概念

  • 項與項集:設itemset={item1, item_2, …,
    item_m}是所有項的集合,其中,item_k(k=1,2,…,m)成為項。項的集合稱為項集(itemset),包含k個項的項集稱為k項集(k-itemset)。

  • 關聯規則:關聯規則是形如A=>B的蘊涵式,其中A、B均為itemset的子集且均不為空集,而A交B為空。

  • 支援度(support):關聯規則的支援度定義如下:
    這裡寫圖片描述
    其中P(AUB)表示事務包含集合A和B的並(即包含A和B中的每個項)的概率。注意與P(A or B)區別,後者表示事務包含A或B的概率。

  • 置信度(confidence):關聯規則的置信度定義如下:
    這裡寫圖片描述

  • 項集的出現頻度(support count):包含項集的事務數,簡稱為項集的頻度、支援度計數或計數。

  • 頻繁項集(frequent itemset):如果項集I的相對支援度滿足事先定義好的最小支援度閾值(即I的出現頻度大於相應的最小出現頻度(支援度計數)閾值),則I是頻繁項集。

  • 強關聯規則:滿足最小支援度和最小置信度的關聯規則,即待挖掘的關聯規則。

3.3 實現步驟
一般而言,關聯規則的挖掘是一個兩步的過程:
1、找出所有的頻繁項集
2、由頻繁項集產生強關聯規則

相關定義

  • 連線步驟:頻繁(k-1)項集Lk-1的自身連線產生候選k項集Ck
    Apriori演算法假定項集中的項按照字典序排序。如果Lk-1中某兩個的元素(項集)itemset1和itemset2的前(k-2)個項是相同的,則稱itemset1和itemset2是可連線的。所以itemset1與itemset2連線產生的結果項集是{itemset1[1], itemset1[2], …, itemset1[k-1], itemset2[k-1]}。連線步驟包含在下文程式碼中的create_Ck函式中。

  • 剪枝策略
    由於存在先驗性質:任何非頻繁的(k-1)項集都不是頻繁k項集的子集。因此,如果一個候選k項集Ck的(k-1)項子集不在Lk-1中,則該候選也不可能是頻繁的,從而可以從Ck中刪除,獲得壓縮後的Ck。下文程式碼中的is_apriori函式用於判斷是否滿足先驗性質,create_Ck函式中包含剪枝步驟,即若不滿足先驗性質,剪枝。

  • 刪除策略
    基於壓縮後的Ck,掃描所有事務,對Ck中的每個項進行計數,然後刪除不滿足最小支援度的項,從而獲得頻繁k項集。刪除策略包含在下文程式碼中的generate_Lk_by_Ck函式中。

找出頻發項集
1、每個項都是候選1項集的集合C1的成員。演算法掃描所有的事務,獲得每個項,生成C1(見下文程式碼中的create_C1函式)。然後對每個項進行計數。然後根據最小支援度從C1中刪除不滿足的項,從而獲得頻繁1項集L1。

2、對L1的自身連線生成的集合執行剪枝策略產生候選2項集的集合C2,然後,掃描所有事務,對C2中每個項進行計數。同樣的,根據最小支援度從C2中刪除不滿足的項,從而獲得頻繁2項集L2。

3、對L2的自身連線生成的集合執行剪枝策略產生候選3項集的集合C3,然後,掃描所有事務,對C3每個項進行計數。同樣的,根據最小支援度從C3中刪除不滿足的項,從而獲得頻繁3項集L3。

4、以此類推,對Lk-1的自身連線生成的集合執行剪枝策略產生候選k項集Ck,然後,掃描所有事務,對Ck中的每個項進行計數。然後根據最小支援度從Ck中刪除不滿足的項,從而獲得頻繁k項集。

樣例
這裡寫圖片描述

本文基於該樣例的資料編寫Python程式碼實現Apriori演算法。程式碼需要注意如下兩點:

  • 由於Apriori演算法假定項集中的項是按字典序排序的,而集合本身是無序的,所以我們在必要時需要進行set和list的轉換;
  • 由於要使用字典(support_data)記錄項集的支援度,需要用項集作為key,而可變集合無法作為字典的key,因此在合適時機應將項集轉為固定集合frozenset。
"""
# Python 2.7
# Filename: apriori.py
# Author: llhthinker
# Email: hangliu56[AT]gmail[DOT]com
# Blog: http://www.cnblogs.com/llhthinker/p/6719779.html
# Date: 2017-04-16
"""


def load_data_set():
    """
    Load a sample data set (From Data Mining: Concepts and Techniques, 3th Edition)
    Returns: 
        A data set: A list of transactions. Each transaction contains several items.
    """
    data_set = [['l1', 'l2', 'l5'], ['l2', 'l4'], ['l2', 'l3'],
            ['l1', 'l2', 'l4'], ['l1', 'l3'], ['l2', 'l3'],
            ['l1', 'l3'], ['l1', 'l2', 'l3', 'l5'], ['l1', 'l2', 'l3']]
    return data_set


def create_C1(data_set):
    """
    Create frequent candidate 1-itemset C1 by scaning data set.
    Args:
        data_set: A list of transactions. Each transaction contains several items.
    Returns:
        C1: A set which contains all frequent candidate 1-itemsets
    """
    C1 = set()
    for t in data_set:
        for item in t:
            item_set = frozenset([item])
            C1.add(item_set)
    return C1


def is_apriori(Ck_item, Lksub1):
    """
    Judge whether a frequent candidate k-itemset satisfy Apriori property.
    Args:
        Ck_item: a frequent candidate k-itemset in Ck which contains all frequent
                 candidate k-itemsets.
        Lksub1: Lk-1, a set which contains all frequent candidate (k-1)-itemsets.
    Returns:
        True: satisfying Apriori property.
        False: Not satisfying Apriori property.
    """
    for item in Ck_item:
        sub_Ck = Ck_item - frozenset([item])
        if sub_Ck not in Lksub1:
            return False
    return True


def create_Ck(Lksub1, k):
    """
    Create Ck, a set which contains all all frequent candidate k-itemsets
    by Lk-1's own connection operation.
    Args:
        Lksub1: Lk-1, a set which contains all frequent candidate (k-1)-itemsets.
        k: the item number of a frequent itemset.
    Return:
        Ck: a set which contains all all frequent candidate k-itemsets.
    """
    Ck = set()
    len_Lksub1 = len(Lksub1)
    list_Lksub1 = list(Lksub1)
    for i in range(len_Lksub1):
        for j in range(1, len_Lksub1):
            l1 = list(list_Lksub1[i])
            l2 = list(list_Lksub1[j])
            l1.sort()
            l2.sort()
            if l1[0:k-2] == l2[0:k-2]:
                Ck_item = list_Lksub1[i] | list_Lksub1[j]
                # pruning
                if is_apriori(Ck_item, Lksub1):
                    Ck.add(Ck_item)
    return Ck


def generate_Lk_by_Ck(data_set, Ck, min_support, support_data):
    """
    Generate Lk by executing a delete policy from Ck.
    Args:
        data_set: A list of transactions. Each transaction contains several items.
        Ck: A set which contains all all frequent candidate k-itemsets.
        min_support: The minimum support.
        support_data: A dictionary. The key is frequent itemset and the value is support.
    Returns:
        Lk: A set which contains all all frequent k-itemsets.
    """
    Lk = set()
    item_count = {}
    for t in data_set:
        for item in Ck:
            if item.issubset(t):
                if item not in item_count:
                    item_count[item] = 1
                else:
                    item_count[item] += 1
    t_num = float(len(data_set))
    for item in item_count:
        if (item_count[item] / t_num) >= min_support:
            Lk.add(item)
            support_data[item] = item_count[item] / t_num
    return Lk


def generate_L(data_set, k, min_support):
    """
    Generate all frequent itemsets.
    Args:
        data_set: A list of transactions. Each transaction contains several items.
        k: Maximum number of items for all frequent itemsets.
        min_support: The minimum support.
    Returns:
        L: The list of Lk.
        support_data: A dictionary. The key is frequent itemset and the value is support.
    """
    support_data = {}
    C1 = create_C1(data_set)
    L1 = generate_Lk_by_Ck(data_set, C1, min_support, support_data)
    Lksub1 = L1.copy()
    L = []
    L.append(Lksub1)
    for i in range(2, k+1):
        Ci = create_Ck(Lksub1, i)
        Li = generate_Lk_by_Ck(data_set, Ci, min_support, support_data)
        Lksub1 = Li.copy()
        L.append(Lksub1)
    return L, support_data


def generate_big_rules(L, support_data, min_conf):
    """
    Generate big rules from frequent itemsets.
    Args:
        L: The list of Lk.
        support_data: A dictionary. The key is frequent itemset and the value is support.
        min_conf: Minimal confidence.
    Returns:
        big_rule_list: A list which contains all big rules. Each big rule is represented
                       as a 3-tuple.
    """
    big_rule_list = []
    sub_set_list = []
    for i in range(0, len(L)):
        for freq_set in L[i]:
            for sub_set in sub_set_list:
                if sub_set.issubset(freq_set):
                    conf = support_data[freq_set] / support_data[freq_set - sub_set]
                    big_rule = (freq_set - sub_set, sub_set, conf)
                    if conf >= min_conf and big_rule not in big_rule_list:
                        # print freq_set-sub_set, " => ", sub_set, "conf: ", conf
                        big_rule_list.append(big_rule)
            sub_set_list.append(freq_set)
    return big_rule_list


if __name__ == "__main__":
    """
    Test
    """
    data_set = load_data_set()
    L, support_data = generate_L(data_set, k=3, min_support=0.2)
    big_rules_list = generate_big_rules(L, support_data, min_conf=0.7)
    for Lk in L:
        print "="*50
        print "frequent " + str(len(list(Lk)[0])) + "-itemsets\t\tsupport"
        print "="*50
        for freq_set in Lk:
            print freq_set, support_data[freq_set]
    print
    print "Big Rules"
    for item in big_rules_list:
        print item[0], "=>", item[1], "conf: ", item[2]

這裡寫圖片描述