数据挖掘: 频繁项集挖掘(购物篮问题)
大家恐怕都聽說過著名的啤酒與尿布, 這是典型的購物籃問題, 在數(shù)據(jù)挖掘界叫做頻繁項集(Frequent Itemsets).
note: 數(shù)據(jù)類型寫法按照Python的格式.
一. 目標(biāo)與定義
1. 問題背景
超市中購物清單中總是有一些項目是被消費者一同購買的. 如果我們能夠發(fā)現(xiàn)這些關(guān)聯(lián)規(guī)則(association rules), 并合理地加以利用, 我們就能取得一定成果. 比如我們發(fā)現(xiàn)熱狗和芥末存在這種關(guān)系, 我們對熱狗降價促銷, 而對芥末適當(dāng)提價, 結(jié)果能顯著提高超市的銷售額.
2. 目標(biāo)
找到頻繁地共同出現(xiàn)在消費者結(jié)賬小票中項目(比如啤酒和尿布), 來一同促銷, 相互拉動, 提高銷售額.
3. 定義
支持度support: 其實就是概率論中的頻次frequency
支持度閾值support threshhold: 記為s, 指分辨頻繁項集的臨界值.
頻繁項集: 如果I是一個項集(Itemset), 且I的出現(xiàn)頻次(i.e.支持度)大于等于s, 那么我們說I是頻繁項集.
一元項, 二元項, 三元項: 包含有一種商品, 兩種, 三種商品的項集.
4. 關(guān)聯(lián)規(guī)則
關(guān)聯(lián)規(guī)則: 形式為I->j, 含義是如果I種所有項都出現(xiàn)在某個購物籃的話, 那么j很有可能也出現(xiàn)在這個購物籃中. 我們可以給出相應(yīng)的confidence值(可信度, 即概率論中的置信度).
其中, 這個關(guān)聯(lián)規(guī)則的可信度計算為Confidence = I∪{j} / I, 本身是非常符合直覺和常識的. 比如我們說關(guān)聯(lián)規(guī)則{dog, cat} -> and 的可信度為0.6, 因為{dog, cat}出現(xiàn)在了1, 2, 3, 6, 7五個購物籃中, 而and出現(xiàn)在了1,2,7中, 因此我們可以算出Confidence = freq[{dog, cat, and}] / freq[{dog, cat}] = 3/5 = 0.6
注意到, 分子部分的頻次總是比分母低, 這是因為{dog, cat} 出現(xiàn)的次數(shù)總是大于等于{dog, cat, and}的出現(xiàn)次數(shù).
二. 購物籃與A-Priori算法
1. 購物籃數(shù)據(jù)表示
我們將有一個文本文件輸入, 比如allBills.txt, 或者allBills.csv. 里面每行是一個購物籃.
文件的頭兩行可能是這樣(df.show(2)):
{23, 456, 1001}
{3, 18, 92, 145}
我們假定這是一家大型連鎖超市, 比如沃爾瑪, 因此這個文本文件是非常大的, 比如20GB. 因此我們無法一次將該文件讀入內(nèi)存. 因此, 算法的主要時間開銷都是磁盤IO.
我們同時還假定, 所有購物籃的平均規(guī)模是較小的, 因此在內(nèi)存中產(chǎn)生所有大小項集的時間開銷會比讀入購物籃的時間少很多.
我們可以計算, 對于有n個項目組成的購物籃而言, 大小為k的所有子集的生成時間約為(n, k) = n! / ((n-k)!k!) = O(n^k/ k!), 其中我們只關(guān)注較小的頻繁項集, 因此我們約定k=2或者k=3. 因此所有子集生成時間T = O(n^3).
Again, 我們認(rèn)為在內(nèi)存中產(chǎn)生所有大小項集的時間開銷會比讀入購物籃的時間少很多.
2. Itemset計數(shù)過程中的內(nèi)存使用
我們必須要把整個k,v字典放在內(nèi)存中, 否則來一個Itemset就去硬盤讀取一次字典將十分十分地慢.
此處, 字典是k=(18, 145), v=15這種形式. 此處, 應(yīng)當(dāng)注意到, 如果有{bread, milk, orange}這樣的String類型輸入, 應(yīng)當(dāng)預(yù)先用一個字典映射成對應(yīng)的整數(shù)值編碼, 比如1920, 4453, 9101這樣.
那么, 我們最多能用字典存儲多少種商品?
先看下我們存儲多少個count值.
我們假定項的總數(shù)目是n, 即超市有n種商品, 每個商品都有一個數(shù)字編號, 那么我們需要(n, 2) = n^2/2 的大小來存儲所有的二元組合的count, 假設(shè)int是占4個byte, 那么需要(2·n^2)Byte內(nèi)存. 已知2GB內(nèi)存 = 2^31 Byte, 即2^31/2 = 2^30 >= n^2 --> n <= 2^15. 也就是說n<33 000, 因此我們說商品種類的最多是33k種.
但是, 這種計算方法存在一個問題, 并不是有10種商品, 那么這10種商品的任意二元組合都會出現(xiàn)的. 對于那些沒出現(xiàn)的組合, 我們在字典中完全可以不存儲, 從而節(jié)省空間.
同時, 別忘了我們同樣也得存儲key = (i, j), 這是至少額外的兩個整數(shù).
那么我們到底具體怎么存儲這些計數(shù)值?
可以采用三元組的方式來構(gòu)造字典. 我們采用[i, j, count]形式來存儲, 其中i代表商品種類1, j代表商品種類2, 前兩個值代表key, 后面的value就是count, 是這個二元組合下的計數(shù).
現(xiàn)在, 讓我們注意到我們(1)假定購物籃平均大小較小, 并(2)利用三元組(2個key的)字典和(3)不存儲沒出現(xiàn)組合優(yōu)勢. 假設(shè)有100k = 10^5種商品, 有10million=10^7個購物籃, 每個購物籃有10個項, 那么這種字典空間開銷是(10, 2) · 10^7 = 45 x 10^7 x 3= 4.5x10^8x3 = 1.35x10^9 個整數(shù). 這算出來約為4x10^8 Byte = 400MB, 處于正常計算機內(nèi)存范圍內(nèi).
3. 項集的單調(diào)性
如果項集I是頻繁的, 那么它的所有子集也都是頻繁的. 這個道理很符合常識, 因為{dog, cat} 出現(xiàn)的次數(shù)總是大于等于{dog, cat, and}的出現(xiàn)次數(shù).
這個規(guī)律的推論, 就是嚴(yán)格地, 我們頻繁一元組的個數(shù)> 頻繁二元組的個數(shù) > 頻繁三元組的個數(shù).
4. A-Priori算法
我們通過Itemset計數(shù)中內(nèi)存使用的部門, 已經(jīng)明確了我們總是有足夠的內(nèi)存用于所有存在的二元項集(比如{cat, dog})的計數(shù). 這里, 我們的字典不存放不存在于購物籃中的任何二元項集合, 而且頻繁二元組的數(shù)目將會大于三元頻繁三元組> ...
我們可以通過單邊掃描購物籃文件, 對于每個購物籃, 我們使用一個雙重循環(huán)就可以生成所有的項對(即二元組). 每當(dāng)我們生成一個項對, 就給其對應(yīng)的字典中的value +1(也稱為計數(shù)器). 最后, 我們會檢查所有項對的計數(shù)結(jié)果,并且找出那些>=閾值s的項對, 他們就是頻繁項對.
1) A-Priori算法的第一遍掃描
在第一遍掃描中, 我們將建立兩個表. 第一張表將項的名稱轉(zhuǎn)換為1到n之間的整數(shù), 從而把String類型這樣的key轉(zhuǎn)為空間大小更小的int類型. 第二張表將記錄從1~n每個項在所有購物籃中出現(xiàn)的次數(shù). 形式上類似
table 0(name table): {'dolphin': 7019, 'cat': 7020} //dict形式, 其實也可以做成list形式 [['dolphin', 7019], ['cat', 7020]]
table 1(single-item counter table): {7019: 15, 7020: 18} //dict形式, 其實也可以做成數(shù)組形式A[7019] = 2, A[7020] = 18
2) 第一遍掃描完的處理
第一遍掃描完后, 我們會按照自己設(shè)定的閾值s, 對整個table 1再進行一次mapping, 因為我們只關(guān)注最后counter值大于等于閾值的項目, 而且不關(guān)心其counter值具體多少. 因此, mapping策略是:
對凡是counter<s的, 一律把counter設(shè)成0; 對于counter>=s的, 按照次序, 把其設(shè)置成1~m的值(總共有m個滿足要求的項)
3) 第二遍掃描
第二遍掃描所做的事有三:
(1) 對每個購物籃, 在table 1中檢查其所有的商品項目, 把所有為頻繁項的留下來建立一個list.
(2) 通過一個雙重循環(huán)生成該list中的所有項對.
(3) 再走一次循環(huán), 在新的數(shù)據(jù)結(jié)構(gòu)table 2(dict或者list)中相應(yīng)的位置+1. 此時的效果是dicta = {48: {13: 5}, 49: {71, 16}} 或者 lista [ [48, 13, 5],[49, 71, 16], ... ]
注意此時內(nèi)存塊上存儲的結(jié)構(gòu): table1(name table), table2(single-item counter table), table3(double-item counter table)
5. 推廣: 任意大小頻繁項集上的A-Priori算法
我們對上面這個算法進行推廣.
從任意集合大小k到下一個大小k+1的轉(zhuǎn)移模式可以這么說:
(1) 對每個購物籃, 在table 1中檢查其所有的商品項目, 把所有為頻繁項的留下來建立一個list.
(2) 我們通過一個k+1重循環(huán)來生成該list中的所有(k+1)元組
(3) 對每個k+1元組, 我們生成其的(k+1 choose k)個k元組, 并檢查這些k元組是否都在之前的table k中. (注意到k=1的時候, 這步與(1)是重復(fù)的, 可以省略)
(4)再走一次循環(huán), 在新的數(shù)據(jù)結(jié)構(gòu)table k+1(dict或者list)中相應(yīng)的位置+1. 此時的效果是k=2, k+1=3, 生成dicta = {48: {13: {19: 4}}, 49: {71: {51: 10}}, ... } 或者 生成lista [ [48, 13, 19, 4],[49, 71, 51, 10], ... ]
注意, 在進入下一次掃描前, 我們還需要額外把counter中值小于s的元組的計數(shù)值都記為0.
模式總體是: C1 過濾后 L1 計數(shù)后 C2 置零后 C2' 過濾后 L2 計數(shù)后 C3 置零后 C3' ......
END.
作者:陳碼工
鏈接:https://www.jianshu.com/p/4aeb6a764804
來源:簡書
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。
總結(jié)
以上是生活随笔為你收集整理的数据挖掘: 频繁项集挖掘(购物篮问题)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 频繁项集挖掘之apriori和fp-gr
- 下一篇: JVM命令参数大全