Apriori算法及超市数据集挖掘实战
文章目錄
- `完整實現代碼在文末哦 !`
- 原理
- 定義
- 代碼實現
- 優化策略
- 構建哈希樹:
- Step1: 遍歷候選集(也就是上面的15個項集)
- Step 2 :
- 使用Hash樹進行支持度計數
- 分桶查找
- 完整超市購物數據挖掘
- 挖掘結果
- 完整實現代碼
完整實現代碼在文末哦 !
原理
定義
◆ 事務T是項i的集合:T={ia,ib,…,it}T=\{i_a, i_b,…,i_t\}T={ia?,ib?,…,it?}
◆ 如果I是所有項的集合,則T是I的子集。
◆ 數據集D是T的集合。
◆ 項集:項的集合。
◆ k項集:k個項的集合。
支持度:描述發生頻次
置信度:衡量規則強度
關聯規則支持度:
𝑆𝑢𝑝𝑝𝑜𝑟𝑡𝑋→𝑌=(𝑋∪𝑌)𝑛𝑆𝑢𝑝𝑝𝑜𝑟𝑡 𝑋 → 𝑌 = \frac{(𝑋 ∪ 𝑌)}{ 𝑛} SupportX→Y=n(X∪Y)?
◆ 關聯規則置信度:
𝐶𝑜𝑛𝑓𝑖𝑑𝑒𝑛𝑐𝑒𝑋→𝑌=(𝑋∪𝑌)(𝑋)𝐶𝑜𝑛𝑓𝑖𝑑𝑒𝑛𝑐𝑒 𝑋 → 𝑌 = \frac{ (𝑋 ∪ 𝑌)} {(𝑋)}ConfidenceX→Y=(X)(X∪Y)?
𝐶𝑜𝑛𝑓𝑖𝑑𝑒𝑛𝑐𝑒𝑋→𝑌=𝑆𝑢𝑝𝑝𝑜𝑟𝑡(𝑋∪𝑌)𝑆𝑢𝑝𝑝𝑜𝑟𝑡(𝑋)𝐶𝑜𝑛𝑓𝑖𝑑𝑒𝑛𝑐𝑒 𝑋 → 𝑌 = \frac{𝑆𝑢𝑝𝑝𝑜𝑟𝑡(𝑋 ∪ 𝑌)}{𝑆𝑢𝑝𝑝𝑜𝑟𝑡(𝑋)}ConfidenceX→Y=Support(X)Support(X∪Y)?
類似于條件概率(在Y出現的情況下X發生的概率)P(X∣Y)=P(XY)P(Y)P(X|Y) = \frac{P(XY)}{P(Y)}P(X∣Y)=P(Y)P(XY)?
課上舉的栗子:
Bread -> Milk :
support=所有項集中出現的該項的次數項集總個數support = \frac{所有項集中出現的該項的次數}{項集總個數}support=項集總個數所有項集中出現的該項的次數?
support:面包和牛奶一起出現的次數 = 2 ; 總項集數 = 8 ;
confidence=X和Y一起出現的次數所有項集中出現的X的次數confidence = \frac{X和Y一起出現的次數}{所有項集中出現的X的次數}confidence=所有項集中出現的X的次數X和Y一起出現的次數?
confidence:有面包出現的項集數 = 6 ;面包牛奶一起出現的次數為2
Milk -> Bread 同理可得
頻繁項集:支持度大于σ\sigmaσ(最小支持度的閾值)的項集
強規則:是頻繁項集且置信度大于Φ(最小置信度)
Task:給定I,D,σ和Φ,挖掘所有強規則的規則
Apriori算法過程:
- 從1到k挨個生成
- 支持度大于σ認可
- 所有可能的關聯規則
- 關聯規則的置信度大于Φ -> 認可
一個項集的子項集數: M=2m?1M = 2^m-1M=2m?1
總項級數: I=∑iN(m?M)I=\sum{_i^N}(m*M) I=∑iN?(m?M)
m : 項集數 ;N:事務數
基本思想:
- 任何非頻繁項集的超集是不頻繁的
- 頻繁項集的所有非空子集一定是頻繁的
咋一看這兩條基本思想沒鳥用,但是真的很有用
隨著物品的增加,計算的次數呈指數的形式增長 …如果減少計數次數?
靠這兩條規則
- 假設現在數據集={0,1,2,3}
- 如果 {0, 1} 是頻繁的,那么 {0}, {1} 也是頻繁的
- {2,3} 是 非頻繁項集,那么利用上面的知識,我們就可以知道 {0,2,3} {1,2,3} {0,1,2,3} 都是 非頻繁的。 也就是說,計算出 {2,3} 的支持度,知道它是 非頻繁 的之后,就不需要再計算 {0,2,3} {1,2,3} {0,1,2,3} 的支持度
流程圖:
代碼實現
核心代碼:
def returnItemsWithMinSupport(itemSet, transactionList, minSupport, freqSet):"""計算子項集的支持度,返回頻繁項集"""_itemSet = set()localSet = defaultdict(int)for item in itemSet:for transaction in transactionList:if item.issubset(transaction):freqSet[item] += 1localSet[item] += 1for item, count in localSet.items():support = float(count) / len(transactionList)if support >= minSupport:_itemSet.add(item)return _itemSetdef getItemSetTransactionList(data_iterator):"""從候選集中獲取所有子項集"""transactionList = list()itemSet = set()for record in data_iterator:transaction = frozenset(record)transactionList.append(transaction)for item in transaction:itemSet.add(frozenset([item])) return itemSet, transactionList參數設置:最小支持度:0.15 最小置信度: 0.6
- 城市名字數據集結果如下:
換一個超市銷售數據集:
頻繁項集:
- 關聯關系挖掘結果
挺好玩的,代碼不是很難,但是網上的代碼都年久失修,我調了很久,更新一些地方的寫法(很暴力的循環,可能增加了復雜度和開銷,太菜了能跑起來就是萬幸了嗚嗚嗚~)
別忘遼~ 記得給個贊!
優化策略
但是提升度會收到零事務的影響
* 減少支持度的計算開銷
* 項集和計數同時都在葉子節點
事務:{1,2,3,5,6}
項集:
在Apriori算法中,當查看一個候選集是否是頻繁項集,需要將該候選集與DB中的每個事務進行比較,如果該候選集在這個事務中出現了,就將其支持度加1。當DB中有5個事務,而候選項集為3個的時候,其總的比較次數就是3×5=15次
為了減少比較的次數,通過以Hash樹的結構來存儲候選集,每一個事務不再和每個候選集進行比較,而是和Hash樹中特定的候選集進行比較
構建哈希樹:
舉例說明:
Step1: 遍歷候選集(也就是上面的15個項集)
對于項集{1,4,5}來說,對第一項 1 來說根據hash函數,應該放在左邊
對于項集{1,2,4}來說,其第一項為1,也放在左邊。
- 也就是說,第一項取模為1的,都放在根節點的左邊
對于項集{4,5,7}來說,第一項為4,放在左邊,但這時因為左邊有3個候選集,需要進行分裂,這時我們根據候選集的第二項進行hash
注意:如果發現一個葉子節點的數目>=3,如果可以hash進行分類就分裂,如果三個數hash結果都一樣,那就放在一起以線性表的結構存起來
- 其余節點依次類推…
Step 2 :
求一個事務可能的子項集,以事務{1,2,3,5,6}為例,求該事務可能存在的三項集。可能的三項集數目應該是C53C_5^3C53?共10個。
- 這一步其實就是對可能的三項集提前進行分桶(為了后面哈希樹的快速查找打好基礎)
- 這樣以來,10個可能的三項集變成6個桶了,計算支持度的時候直接去對應hash桶里找,然后比較
使用Hash樹進行支持度計數
構建的hash樹就長這樣
分桶查找
待支持度計數的項集
- 對于事務{1,2,3,5,6},首項為1的項集,應該投遞在左邊,而首項為2的投遞在中間,首項為3的投遞在右邊
- {1,2,3},{1,2,6}應該投遞在右,與{1,5,9}葉子節點中數據進行比較,沒有相同的,支持度為0
- {1,2,5}應該投遞在中間,與{1,2,5},{4,5,8}結點進行比較,存在項集{1,2,5},因此{1,2,5}的支持度計數加1
對于項集前兩項為1,3的前提下,因為只有一個葉子節點,因此不需要再對第三項進行投遞,因此直接和{1,3,6}葉子節點進行比較,{1,3,6}有相同的,{1,3,6}支持度+1
對于項集前兩項為1,5的前提下,只有一個葉子節點{1,5,6},不需要投遞,直接和{1,5,6}比較,不相同,支持度為0
對于項集前兩項為{2,3},{2,5}的前提下,只有兩個節點{2,3,4}{5,6,7},沒有相同的,支持度為0
對于項集前兩項為3,5的前提下,有三個節點{3,5,6},{3,5,7},{6,8,9},有一個相同的,{3,5,6}支持度+1
- 紅框內就是需要比較的項集,紅框內一共9個項集,也就是算支持度的時候只有比較9次,而不是逐一遍歷的15次
所以,最后得到的頻繁項集為{1,2,5},{3,5,6},{1,3,6}
完整超市購物數據挖掘
算法參數:
置信度Confidence = 0.4 支持度Support = 0.20
- 頻繁項集支持度計算:
注意到Lassi了么!!!, 我反正沒見過,這是美國的超市數據集
- wiki以下Lassi
查閱Lassi的資料和組成成分發現,,Lassi與Milk、Sweet的組合出現不是偶然,是必然!
挖掘結果
- Milk 單品銷售最頻繁,單品銷售冠軍;Milk的銷售組合中,與Sugar、Lassi的組合銷售占最高,可以通過Milk來間接增加Sugar與Lassi的銷售量
- Sweet與Lassi的組合在各頻繁項集中support最高,且單品support也很高,說明在購物數據集中以上集中商品,Sweet+Lassi可以出現在各個銷售組合中,是組合銷售冠軍,可以通過Sweet和Lassi的與其他商品捆綁做打折促銷,能最大程度的將賣不出去的其他商品清倉,以獲取最高的利潤
完整實現代碼
用命令行來啟動:python apriori.py 數據集 -c 置信度 -s 支持度
import sysfrom itertools import chain, combinations from collections import defaultdict from optparse import OptionParserdef subsets(arr):return chain(*[combinations(arr, i + 1) for i, a in enumerate(arr)])def returnItemsWithMinSupport(itemSet, transactionList, minSupport, freqSet):_itemSet = set()localSet = defaultdict(int)for item in itemSet:for transaction in transactionList:if item.issubset(transaction):freqSet[item] += 1localSet[item] += 1for item, count in localSet.items():support = float(count) / len(transactionList)if support >= minSupport:_itemSet.add(item)return _itemSetdef joinSet(itemSet, length):"""Join a set with itself and returns the n-element itemsets"""return set([i.union(j) for i in itemSet for j in itemSet if len(i.union(j)) == length])def getItemSetTransactionList(data_iterator):transactionList = list()itemSet = set()for record in data_iterator:transaction = frozenset(record)transactionList.append(transaction)for item in transaction:itemSet.add(frozenset([item])) # Generate 1-itemSetsreturn itemSet, transactionListdef runApriori(data_iter, minSupport, minConfidence):itemSet, transactionList = getItemSetTransactionList(data_iter)freqSet = defaultdict(int)largeSet = dict()assocRules = dict()oneCSet = returnItemsWithMinSupport(itemSet, transactionList, minSupport, freqSet)currentLSet = oneCSetk = 2while currentLSet != set([]):largeSet[k - 1] = currentLSetcurrentLSet = joinSet(currentLSet, k)currentCSet = returnItemsWithMinSupport(currentLSet, transactionList, minSupport, freqSet)currentLSet = currentCSetk = k + 1def getSupport(item):"""local function which Returns the support of an item"""return float(freqSet[item]) / len(transactionList)toRetItems = []for key, value in largeSet.items():toRetItems.extend([(tuple(item), getSupport(item)) for item in value])toRetRules = []for key, value in list(largeSet.items())[1:]:for item in value:_subsets = map(frozenset, [x for x in subsets(item)])for element in _subsets:remain = item.difference(element)if len(remain) > 0:confidence = getSupport(item) / getSupport(element)if confidence >= minConfidence:toRetRules.append(((tuple(element), tuple(remain)), confidence))return toRetItems, toRetRulesdef printResults(items, rules):for item, support in sorted(items, key=lambda x: x[1]):print("item: %s , %.3f" % (str(item), support))print("\n------------------------ RULES:")for rule, confidence in sorted(rules, key=lambda x: x[1]):pre, post = ruleprint("Rule: %s ==> %s , %.3f" % (str(pre), str(post), confidence))def to_str_results(items, rules):i, r = [], []for item, support in sorted(items, key=lambda x: x[1]):x = "item: %s , %.3f" % (str(item), support)i.append(x)for rule, confidence in sorted(rules, key=lambda x: x[1]):pre, post = rulex = "Rule: %s ==> %s , %.3f" % (str(pre), str(post), confidence)r.append(x)return i, rdef dataFromFile(fname):with open(fname, "rU") as file_iter:for line in file_iter:line = line.strip().rstrip(",") # Remove trailing commarecord = frozenset(line.split(","))yield recordif __name__ == "__main__":optparser = OptionParser()optparser.add_option("-f", "--inputFile", dest="input", help="filename containing csv", default=None)optparser.add_option("-s","--minSupport",dest="minS",help="minimum support value",default=0.15,type="float",)optparser.add_option("-c","--minConfidence",dest="minC",help="minimum confidence value",default=0.6,type="float",)(options, args) = optparser.parse_args()inFile = Noneif options.input is None:inFile = sys.stdinelif options.input is not None:inFile = dataFromFile(options.input)else:print("No dataset filename specified, system with exit\n")sys.exit("System will exit")minSupport = options.minSminConfidence = options.minCitems, rules = runApriori(inFile, minSupport, minConfidence)printResults(items, rules)總結
以上是生活随笔為你收集整理的Apriori算法及超市数据集挖掘实战的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用nohup运行循环脚本插入发现数据重
- 下一篇: bootstrap 模态框弹出就消失了_