1、 搞懂關聯規則中的幾個概念html
關聯規則這個概念,最先是由 Agrawal 等人在 1993 年提出的。在 1994 年 Agrawal 等人又提出了基於關聯規則的 Apriori 算法,至今 Apriori 還是關聯規則挖掘的重要算法。python
/*請尊重做者勞動成果,轉載請標明原文連接:*/web
/* https://www.cnblogs.com/jpcflyer/p/11146587.html * /算法
關聯規則挖掘可讓咱們從數據集中發現項與項(item 與 item)之間的關係,它在咱們的生活中有不少應用場景,「購物籃分析」就是一個常見的場景,這個場景能夠從消費者交易記錄中發掘商品與商品之間的關聯關係,進而經過商品捆綁銷售或者相關推薦的方式帶來更多的銷售量。因此說,關聯規則挖掘是個很是有用的技術。數組
我舉一個超市購物的例子,下面是幾名客戶購買的商品列表:瀏覽器
什麼是支持度呢?app
支持度是個百分比,它指的是某個商品組合出現的次數與總次數之間的比例。支持度越高,表明這個組合出現的頻率越大。ide
在這個例子中,咱們能看到「牛奶」出現了 4 次,那麼這 5 筆訂單中「牛奶」的支持度就是 4/5=0.8。函數
一樣「牛奶 + 麪包」出現了 3 次,那麼這 5 筆訂單中「牛奶 + 麪包」的支持度就是 3/5=0.6。工具
什麼是置信度呢?
它指的就是當你購買了商品 A,會有多大的機率購買商品 B,在上面這個例子中:
置信度(牛奶→啤酒)=2/4=0.5,表明若是你購買了牛奶,有多大的機率會購買啤酒?
置信度(啤酒→牛奶)=2/3=0.67,表明若是你購買了啤酒,有多大的機率會購買牛奶?
咱們能看到,在 4 次購買了牛奶的狀況下,有 2 次購買了啤酒,因此置信度 (牛奶→啤酒)=0.5,而在 3 次購買啤酒的狀況下,有 2 次購買了牛奶,因此置信度(啤酒→牛奶)=0.67。
因此說置信度是個條件概念,就是說在 A 發生的狀況下,B 發生的機率是多少。
什麼是提高度呢?
咱們在作商品推薦的時候,重點考慮的是提高度,由於提高度表明的是「商品 A 的出現,對商品 B 的出現機率提高的」程度。
仍是看上面的例子,若是咱們單純看置信度 (可樂→尿布)=1,也就是說可樂出現的時候,用戶都會購買尿布,那麼當用戶購買可樂的時候,咱們就須要推薦尿布麼?
實際上,就算用戶不購買可樂,也會直接購買尿布的,因此用戶是否購買可樂,對尿布的提高做用並不大。咱們能夠用下面的公式來計算商品 A 對商品 B 的提高度:
提高度 (A→B)= 置信度 (A→B)/ 支持度 (B)
這個公式是用來衡量 A 出現的狀況下,是否會對 B 出現的機率有所提高。
因此提高度有三種可能:
提高度 (A→B)>1:表明有提高;
提高度 (A→B)=1:表明有沒有提高,也沒有降低;
提高度 (A→B)<1:表明有降低。
2、 Apriori 的工做原理
明白了關聯規則中支持度、置信度和提高度這幾個重要概念,咱們來看下 Apriori 算法是如何工做的。
首先咱們把上面案例中的商品用 ID 來表明,牛奶、麪包、尿布、可樂、啤酒、雞蛋的商品 ID 分別設置爲 1-6,上面的數據表能夠變爲:
Apriori 算法其實就是查找頻繁項集 (frequent itemset) 的過程,因此首先咱們須要定義什麼是頻繁項集。
頻繁項集就是支持度大於等於最小支持度 (Min Support) 閾值的項集,因此小於最小值支持度的項目就是非頻繁項集,而大於等於最小支持度的項集就是頻繁項集。
項集這個概念,英文叫作 itemset,它能夠是單個的商品,也能夠是商品的組合。咱們再來看下這個例子,假設我隨機指定最小支持度是 50%,也就是 0.5。
咱們來看下 Apriori 算法是如何運算的。
首先,咱們先計算單個商品的支持度,也就是獲得 K=1 項的支持度:
由於最小支持度是 0.5,因此你能看到商品 四、6 是不符合最小支持度的,不屬於頻繁項集,因而通過篩選商品的頻繁項集就變成:
在這個基礎上,咱們將商品兩兩組合,獲得 k=2 項的支持度:
咱們再篩掉小於最小值支持度的商品組合,能夠獲得:
咱們再將商品進行 K=3 項的商品組合,能夠獲得:
再篩掉小於最小值支持度的商品組合,能夠獲得:
到這裏,你已經和我模擬了一遍整個 Apriori 算法的流程,下面我來給你總結下 Apriori 算法的遞歸流程:
K=1,計算 K 項集的支持度;
篩選掉小於最小支持度的項集;
若是項集爲空,則對應 K-1 項集的結果爲最終結果。
不然 K=K+1,重複 1-3 步。
3、 Apriori 的改進算法:FP-Growth 算法
能看到 Apriori 在計算的過程當中有如下幾個缺點:
可能產生大量的候選集。由於採用排列組合的方式,把可能的項集都組合出來了;
每次計算都須要從新掃描數據集,來計算每一個項集的支持度。
因此 Apriori 算法會浪費不少計算空間和計算時間,爲此人們提出了 FP-Growth 算法,它的特色是:
建立了一棵 FP 樹來存儲頻繁項集。在建立前對不知足最小支持度的項進行刪除,減小了存儲空間。我稍後會講解如何構造一棵 FP 樹;
整個生成過程只遍歷數據集 2 次,大大減小了計算量。
因此在實際工做中,咱們經常使用 FP-Growth 來作頻繁項集的挖掘,下面我給你簡述下 FP-Growth 的原理。
1. 建立項頭表(item header table)
建立項頭表的做用是爲 FP 構建及頻繁項集挖掘提供索引。
這一步的流程是先掃描一遍數據集,對於知足最小支持度的單個項(K=1 項集)按照支持度從高到低進行排序,這個過程當中刪除了不知足最小支持度的項。
項頭表包括了項目、支持度,以及該項在 FP 樹中的鏈表。初始的時候鏈表爲空。
2. 構造 FP 樹
FP 樹的根節點記爲 NULL 節點。
整個流程是須要再次掃描數據集,對於每一條數據,按照支持度從高到低的順序進行建立節點(也就是第一步中項頭表中的排序結果),節點若是存在就將計數 count+1,若是不存在就進行建立。同時在建立的過程當中,須要更新項頭表的鏈表。
3. 經過 FP 樹挖掘頻繁項集
到這裏,咱們就獲得了一個存儲頻繁項集的 FP 樹,以及一個項頭表。咱們能夠經過項頭表來挖掘出每一個頻繁項集。
具體的操做會用到一個概念,叫「條件模式基」,它指的是以要挖掘的節點爲葉子節點,自底向上求出 FP 子樹,而後將 FP 子樹的祖先節點設置爲葉子節點之和。
我以「啤酒」的節點爲例,從 FP 樹中能夠獲得一棵 FP 子樹,將祖先節點的支持度記爲葉子節點之和,獲得:
你能看出來,相比於原來的 FP 樹,尿布和牛奶的頻繁項集數減小了。這是由於咱們求得的是以「啤酒」爲節點的 FP 子樹,也就是說,在頻繁項集中必定要含有「啤酒」這個項。你能夠再看下原始的數據,其中訂單 1{牛奶、麪包、尿布}和訂單 5{牛奶、麪包、尿布、可樂}並不存在「啤酒」這個項,因此針對訂單 1,尿布→牛奶→麪包這個項集就會從 FP 樹中去掉,針對訂單 5 也包括了尿布→牛奶→麪包這個項集也會從 FP 樹中去掉,因此你能看到以「啤酒」爲節點的 FP 子樹,尿布、牛奶、麪包項集上的計數比原來少了 2。
條件模式基不包括「啤酒」節點,並且祖先節點若是小於最小支持度就會被剪枝,因此「啤酒」的條件模式基爲空。
同理,咱們能夠求得「麪包」的條件模式基爲:
因此能夠求得麪包的頻繁項集爲{尿布,麪包},{尿布,牛奶,麪包}。一樣,咱們還能夠求得牛奶,尿布的頻繁項集,這裏就再也不計算展現。
4、 如何使用 Apriori 工具包
Apriori 雖然是十大算法之一,不過在 sklearn 工具包中並無它,也沒有 FP-Growth 算法。這裏教你個方法,來選擇 Python 中可使用的工具包,你能夠經過 https://pypi.org/ 搜索工具包。
這個網站提供的工具包都是 Python 語言的,你能找到 8 個 Python 語言的 Apriori 工具包,具體選擇哪一個呢?建議你使用第二個工具包,即 efficient-apriori。後面我會講到爲何推薦這個工具包。
首先你須要經過 pip install efficient-apriori 安裝這個工具包。
而後看下如何使用它,核心的代碼就是這一行:
1 itemsets, rules = apriori(data, min_support, min_confidence)
其中 data 是咱們要提供的數據集,它是一個 list 數組類型。min_support 參數爲最小支持度,在 efficient-apriori 工具包中用 0 到 1 的數值表明百分比,好比 0.5 表明最小支持度爲 50%。min_confidence 是最小置信度,數值也表明百分比,好比 1 表明 100%。
接下來咱們用這個工具包,跑一下前面講到的超市購物的例子。下面是客戶購買的商品列表:
具體實現的代碼以下:
1 from efficient_apriori import apriori 2 # 設置數據集 3 data = [('牛奶','麪包','尿布'), 4 ('可樂','麪包', '尿布', '啤酒'), 5 ('牛奶','尿布', '啤酒', '雞蛋'), 6 ('麪包', '牛奶', '尿布', '啤酒'), 7 ('麪包', '牛奶', '尿布', '可樂')] 8 # 挖掘頻繁項集和頻繁規則 9 itemsets, rules = apriori(data, min_support=0.5, min_confidence=1) 10 print(itemsets) 11 print(rules)
運行結果:
1 {1: {('啤酒',): 3, ('尿布',): 5, ('牛奶',): 4, ('麪包',): 4}, 2: {('啤酒', '尿布'): 3, ('尿布', '牛奶'): 4, ('尿布', '麪包'): 4, ('牛奶', '麪包'): 3}, 3: {('尿布', '牛奶', '麪包'): 3}} 2 [{啤酒} -> {尿布}, {牛奶} -> {尿布}, {麪包} -> {尿布}, {牛奶, 麪包} -> {尿布}]
你能從代碼中看出來,data 是個 List 數組類型,其中每一個值均可以是一個集合。實際上你也能夠把 data 數組中的每一個值設置爲 List 數組類型,好比:
1 data = [['牛奶','麪包','尿布'], 2 ['可樂','麪包', '尿布', '啤酒'], 3 ['牛奶','尿布', '啤酒', '雞蛋'], 4 ['麪包', '牛奶', '尿布', '啤酒'], 5 ['麪包', '牛奶', '尿布', '可樂']]
二者的運行結果是同樣的,efficient-apriori 工具包把每一條數據集裏的項式都放到了一個集合中進行運算,並無考慮它們之間的前後順序。由於實際狀況下,同一個購物籃中的物品也不須要考慮購買的前後順序。
而其餘的 Apriori 算法可能會由於考慮了前後順序,出現計算頻繁項集結果不對的狀況。因此這裏採用的是 efficient-apriori 這個工具包。
5、 挖掘導演是如何選擇演員的
在實際工做中,數據集是須要本身來準備的,好比咱們要挖掘導演是如何選擇演員的數據狀況,可是並無公開的數據集能夠直接使用。所以咱們須要使用以前講到的 Python 爬蟲進行數據採集。
不一樣導演選擇演員的規則是不一樣的,所以咱們須要先指定導演。數據源咱們選用豆瓣電影。
先來梳理下采集的工做流程。
首先咱們先在 https://movie.douban.com 搜索框中輸入導演姓名,好比「甯浩」。
頁面會呈現出來導演以前的全部電影,而後對頁面進行觀察,你能觀察到如下幾個現象:
頁面默認是 15 條數據反饋,第一頁會返回 16 條。由於第一條數據實際上這個導演的概覽,你能夠理解爲是一條廣告的插入,下面纔是真正的返回結果。
每條數據的最後一行是電影的演出人員的信息,第一我的員是導演,其他爲演員姓名。姓名之間用「/」分割。
有了這些觀察以後,咱們就能夠編寫抓取程序了。在代碼講解中你能看出這兩點觀察的做用。抓取程序的目的是爲了生成甯浩導演(你也能夠抓取其餘導演)的數據集,結果會保存在 csv 文件中。完整的抓取代碼以下:
1 # -*- coding: utf-8 -*- 2 # 下載某個導演的電影數據集 3 from efficient_apriori import apriori 4 from lxml import etree 5 import time 6 from selenium import webdriver 7 import csv 8 driver = webdriver.Chrome() 9 # 設置想要下載的導演 數據集 10 director = u'甯浩' 11 # 寫 CSV 文件 12 file_name = './' + director + '.csv' 13 base_url = 'https://movie.douban.com/subject_search?search_text='+director+'&cat=1002&start=' 14 out = open(file_name,'w', newline='', encoding='utf-8-sig') 15 csv_write = csv.writer(out, dialect='excel') 16 flags=[] 17 # 下載指定頁面的數據 18 def download(request_url): 19 driver.get(request_url) 20 time.sleep(1) 21 html = driver.find_element_by_xpath("//*").get_attribute("outerHTML") 22 html = etree.HTML(html) 23 # 設置電影名稱,導演演員 的 XPATH 24 movie_lists = html.xpath("/html/body/div[@id='wrapper']/div[@id='root']/div[1]//div[@class='item-root']/div[@class='detail']/div[@class='title']/a[@class='title-text']") 25 name_lists = html.xpath("/html/body/div[@id='wrapper']/div[@id='root']/div[1]//div[@class='item-root']/div[@class='detail']/div[@class='meta abstract_2']") 26 # 獲取返回的數據個數 27 num = len(movie_lists) 28 if num > 15: # 第一頁會有 16 條數據 29 # 默認第一個不是,因此須要去掉 30 movie_lists = movie_lists[1:] 31 name_lists = name_lists[1:] 32 for (movie, name_list) in zip(movie_lists, name_lists): 33 # 會存在數據爲空的狀況 34 if name_list.text is None: 35 continue 36 # 顯示下演員名稱 37 print(name_list.text) 38 names = name_list.text.split('/') 39 # 判斷導演是否爲指定的 director 40 if names[0].strip() == director and movie.text not in flags: 41 # 將第一個字段設置爲電影名稱 42 names[0] = movie.text 43 flags.append(movie.text) 44 csv_write.writerow(names) 45 print('OK') # 表明這頁數據下載成功 46 print(num) 47 if num >= 14: # 有可能一頁會有 14 個電影 48 # 繼續下一頁 49 return True 50 else: 51 # 沒有下一頁 52 return False 53 54 # 開始的 ID 爲 0,每頁增長 15 55 start = 0 56 while start<10000: # 最多抽取 1 萬部電影 57 request_url = base_url + str(start) 58 # 下載數據,並返回是否有下一頁 59 flag = download(request_url) 60 if flag: 61 start = start + 15 62 else: 63 break 64 out.close() 65 print('finished')
代碼中涉及到了幾個模塊,我簡單講解下這幾個模塊。
在引用包這一段,咱們使用 csv 工具包讀寫 CSV 文件,用 efficient_apriori 完成 Apriori 算法,用 lxml 進行 XPath 解析,time 工具包可讓咱們在模擬後有個適當停留,代碼中我設置爲 1 秒鐘,等 HTML 數據徹底返回後再進行 HTML 內容的獲取。使用 selenium 的 webdriver 來模擬瀏覽器的行爲。
在讀寫文件這一塊,咱們須要事先告訴 python 的 open 函數,文件的編碼是 utf-8-sig(對應代碼:encoding=‘utf-8-sig’),這是由於咱們會用到中文,爲了不編碼混亂。
編寫 download 函數,參數傳入咱們要採集的頁面地址(request_url)。針對返回的 HTML,咱們須要用到以前講到的 Chrome 瀏覽器的 XPath Helper 工具,來獲取電影名稱以及演出人員的 XPath。我用頁面返回的數據個數來判斷當前所處的頁面序號。若是數據個數 >15,也就是第一頁,第一頁的第一條數據是廣告,咱們須要忽略。若是數據個數 =15,表明是中間頁,須要點擊「下一頁」,也就是翻頁。若是數據個數 <15,表明最後一頁,沒有下一頁。
在程序主體部分,咱們設置 start 表明抓取的 ID,從 0 開始最多抓取 1 萬部電影的數據(一個導演不會超過 1 萬部電影),每次翻頁 start 自動增長 15,直到 flag=False 爲止,也就是不存在下一頁的狀況。
你能夠模擬下抓取的流程,得到指定導演的數據,好比我上面抓取的甯浩的數據。這裏須要注意的是,豆瓣的電影數據多是不全的,但基本上夠咱們用。
有了數據以後,咱們就能夠用 Apriori 算法來挖掘頻繁項集和關聯規則,代碼以下:
1 # -*- coding: utf-8 -*- 2 from efficient_apriori import apriori 3 import csv 4 director = u'甯浩' 5 file_name = './'+director+'.csv' 6 lists = csv.reader(open(file_name, 'r', encoding='utf-8-sig')) 7 # 數據加載 8 data = [] 9 for names in lists: 10 name_new = [] 11 for name in names: 12 # 去掉演員數據中的空格 13 name_new.append(name.strip()) 14 data.append(name_new[1:]) 15 # 挖掘頻繁項集和關聯規則 16 itemsets, rules = apriori(data, min_support=0.5, min_confidence=1) 17 print(itemsets) 18 print(rules)
代碼中使用的 apriori 方法和開頭中用 Apriori 獲取購物籃規律的方法相似,好比代碼中都設定了最小支持度和最小置信係數,這樣咱們能夠找到支持度大於 50%,置信係數爲 1 的頻繁項集和關聯規則。
這是最後的運行結果:
1 {1: {('徐崢',): 5, ('黃渤',): 6}, 2: {('徐崢', '黃渤'): 5}} 2 [{徐崢} -> {黃渤}]
你能看出來,甯浩導演喜歡用徐崢和黃渤,而且有徐崢的狀況下,通常都會用黃渤。你也能夠用上面的代碼來挖掘下其餘導演選擇演員的規律。