当前位置:   article > 正文

关联规则:Apriori算法【“频繁项”集挖掘算法】【迭代法:①搜出候选1项集,剪枝得频繁1项集;②对剩下频繁1项集进行连接得2项集,剪枝得频繁2项集..】【剪枝:根据设置的支持度滤掉小于该值的项集】_频繁关联规则例题

频繁关联规则例题

缺点:由频繁k-1项集进行自连接生成的候选频繁k项集数量巨大,耗时。

Aprior算法是一个非常经典的频繁项集的挖掘算法,很多算法都是基于Aprior算法而产生的,包括FP-Tree,GSP, CBA等。这些算法利用了Aprior算法的思想,但是对算法做了改进,数据挖掘效率更好一些,因此现在一般很少直接用Aprior算法来挖掘数据了,但是理解Aprior算法是理解其它Aprior类算法的前提,同时算法本身也不复杂,因此值得好好研究一番。

不过scikit-learn中并没有频繁集挖掘相关的算法类库,这不得不说是一个遗憾,不知道后面的版本会不会加上。

一、Apriori算法思想

对于Apriori算法,我们使用支持度来作为我们判断频繁项集的标准。Apriori算法的目标是找到最大的K项频繁集。这里有两层意思,首先,我们要找到符合支持度标准的频繁集。但是这样的频繁集可能有很多。第二层意思就是我们要找到最大个数的频繁集。比如我们找到符合支持度的频繁集AB和ABE,那么我们会抛弃AB,只保留ABE,因为AB是2项频繁集,而ABE是3项频繁集。那么具体的,Apriori算法是如何做到挖掘K项频繁集的呢?

Apriori算法采用了迭代的方法,先搜索出候选1项集及对应的支持度,剪枝去掉低于支持度的1项集,得到频繁1项集。然后对剩下的频繁1项集进行连接,得到候选的频繁2项集,筛选去掉低于支持度的候选频繁2项集,得到真正的频繁二项集,以此类推,迭代下去,直到无法找到频繁k+1项集为止,对应的频繁k项集的集合即为算法的输出结果。

可见这个算法还是很简洁的,第i次的迭代过程包括扫描计算候选频繁i项集的支持度,剪枝得到真正频繁i项集和连接生成候选频繁i+1项集三步。

我们下面这个简单的例子看看:

我们的数据集D有4条记录,分别是134,235,1235和25。现在我们用Apriori算法来寻找频繁k项集,最小支持度设置为50%。首先我们生成候选频繁1项集,包括我们所有的5个数据并计算5个数据的支持度,计算完毕后我们进行剪枝,数据4由于支持度只有25%被剪掉。我们最终的频繁1项集为1235,现在我们链接生成候选频繁2项集,包括12,13,15,23,25,35共6组。此时我们的第一轮迭代结束。

进入第二轮迭代,我们扫描数据集计算候选频繁2项集的支持度,接着进行剪枝,由于12和15的支持度只有25%而被筛除,得到真正的频繁2项集,包括13,23,25,35。现在我们链接生成候选频繁3项集,123, 135和235共3组,这部分图中没有画出。通过计算候选频繁3项集的支持度,我们发现123和135的支持度均为25%,因此接着被剪枝,最终得到的真正频繁3项集为235一组。由于此时我们无法再进行数据连接,进而得到候选频繁4项集,最终的结果即为频繁3三项集235。

二、Aprior算法流程

下面我们对Aprior算法流程做一个总结。输入:数据集合D,支持度阈值α;输出:最大的频繁k项集;

1)扫描整个数据集,得到所有出现过的数据,作为候选频繁1项集。k=1,频繁0项集为空集。

2)挖掘频繁k项集

   a) 扫描数据计算候选频繁k项集的支持度

   b) 去除候选频繁k项集中支持度低于阈值的数据集,得到频繁k项集。如果得到的频繁k项集为空,则直接返回频繁k-1项集的集合作为算法结果,算法结束。如果得到的频繁k项集只有一项,则直接返回频繁k项集的集合作为算法结果,算法结束。

   c) 基于频繁k项集,连接生成候选频繁k+1项集。

3) 令k=k+1,转入步骤2。

从算法的步骤可以看出,Aprior算法每轮迭代都要扫描数据集,因此在数据集很大,数据种类很多的时候,算法效率很低。

三、Aprior算法介绍

假设现在有 4 个项(0、1、2、3),那么需要找出频繁项集的话就需要遍历所有可能的项集,一共15个(如下图所示)

在这里插入图片描述
假设现在有 n nn 个项,那么所有可能的项集就有 2 n − 1 2^n-12n−1 个,显然,当 n nn 较大时,采用暴力遍历的方法寻找频繁项集是不可行的。所以就有人提出了 Apriori 算法来减少遍历的次数。

首先,我们要知道一个定理,如果一个项集是非频繁项集,那么它的超集一定也是非频繁的这个定理很容易理解,比如项集{2,3}是非频繁的,那么它的一个超集{1,2,3}肯定也是非频繁的,为什么呢?因为{1,2,3}出现的概率肯定小于等于{2,3}出现的概率,如果{2,3}都是非频繁的,那么出现概率更小的{1,2,3}当然也是非频繁的啦!

Apriori 算法的思想就是,在遍历的时候采用上述定理进行剪枝,从而减少遍历次数。

如下图所示,采用 Apriori 算法,假设项集{2,3}是非频繁的,那么项集{023}、{123}和{0123}肯定都是非频繁的。所以可以不用遍历它们。

四、Python代码实现

案例:求下图所示数据集的频繁项集并计算规则

代码
  1. import math
  2. import time
  3. def get_item_set(data):
  4. '''
  5. 获取项的字典
  6. :param data: 数据集
  7. :return: 项的字典
  8. '''
  9. item_set = set()
  10. for d in data:
  11. item_set = item_set | set(d)
  12. return item_set
  13. def apriori(item_set, data, min_support=0.50):
  14. '''
  15. 获取频繁项集
  16. :param item_set: 项的字典
  17. :param data: 数据集
  18. :param min_support: 最小支持度,默认为0.50
  19. :return: None
  20. '''
  21. # 初始化存储非频繁项集的列表
  22. infrequent_list = []
  23. # 初始化存储频繁项集的列表
  24. frequent_list = []
  25. # 初始化存储频繁项集的支持度的列表
  26. frequent_support_list = []
  27. # 遍历获取 n-项集
  28. for n in range(1, len(item_set) + 1):
  29. c = []
  30. supports = []
  31. if len(frequent_list) == 0:
  32. # 计算 1-项集
  33. for item in item_set:
  34. items = {item}
  35. support = calc_support(data, items)
  36. # 如果支持度大于等于最小支持度就为频繁项集
  37. if support >= min_support:
  38. c.append(items)
  39. supports.append(support)
  40. else:
  41. infrequent_list.append(items)
  42. else:
  43. # 计算 n-项集,n > 1
  44. for last_items in frequent_list[-1]:
  45. for item in item_set:
  46. if item > list(last_items)[-1]:
  47. items = last_items.copy()
  48. items.add(item)
  49. # 如果items的子集没有非频繁项集才计算支持度
  50. if is_infrequent(infrequent_list, items) is False:
  51. support = calc_support(data, items)
  52. # 如果支持度大于等于最小支持度就为频繁项集
  53. if support >= min_support:
  54. c.append(items)
  55. supports.append(support)
  56. else:
  57. infrequent_list.append(items)
  58. frequent_list.append(c)
  59. frequent_support_list.append(supports)
  60. print(f"{n}-项集: {c} , 支持度分别为: {supports}")
  61. return infrequent_list, frequent_list, frequent_support_list
  62. def is_infrequent(infrequent_list, items):
  63. '''
  64. 判断是否属于非频繁项集的超集
  65. :param infrequent_list: 非频繁项集列表
  66. :param items: 项集
  67. :return: 是否属于非频繁项集的超集
  68. '''
  69. for infrequent in infrequent_list:
  70. if infrequent.issubset(items):
  71. return True
  72. return False
  73. def calc_support(data, items):
  74. '''
  75. 计算 support
  76. :param data: 数据集
  77. :param items: 项集
  78. :return: 计算好的支持度
  79. '''
  80. cnt = 0
  81. for d in data:
  82. if items.issubset(d):
  83. cnt += 1
  84. return cnt / len(data)
  85. def generate_rules(frequent_list, data, min_confidence=0.60):
  86. '''
  87. 根据频繁项集和最小置信度生成规则
  88. :param frequent_list: 存储频繁项集的列表
  89. :param data: 数据集
  90. :param min_confidence: 最小置信度
  91. :return: 规则
  92. '''
  93. rule_key_set = set()
  94. rules = []
  95. for frequent in frequent_list:
  96. for items in frequent:
  97. if len(items) > 1:
  98. for n in range(1, math.ceil(len(items) / 2) + 1):
  99. front_set_list = get_all_combine(list(items), n)
  100. for front_set in front_set_list:
  101. back_set = items - front_set
  102. confidence = calc_confidence(front_set, items, data)
  103. if confidence >= min_confidence:
  104. rule = (front_set, back_set, confidence)
  105. key = f'{front_set} ==> {back_set} , confidence: {confidence}'
  106. if key not in rule_key_set:
  107. rule_key_set.add(key)
  108. rules.append(rule)
  109. print(f"规则{len(rules)}: {key}")
  110. return rules
  111. def get_all_combine(data_set, length):
  112. '''
  113. 在指定数据集种获取指定长度的所有组合
  114. :param data_set: 数据集
  115. :param length: 指定的长度
  116. :return: 所有符合约束的组合
  117. '''
  118. def dfs(cur_index, cur_arr):
  119. if cur_index < len(data_set):
  120. cur_arr.append(data_set[cur_index])
  121. if len(cur_arr) == length:
  122. combine_list.append(set(cur_arr))
  123. else:
  124. for index in range(cur_index + 1, len(data_set)):
  125. dfs(index, cur_arr.copy())
  126. combine_list = []
  127. for start_index in range(len(data_set)):
  128. dfs(start_index, [])
  129. return combine_list
  130. def calc_confidence(front_set, total_set, data):
  131. '''
  132. 计算规则 X==>Y 的置信度
  133. :param front_set: X
  134. :param total_set: X ∪ Y
  135. :param data: 数据集
  136. :return: 返回规则 X==>Y 的置信度
  137. '''
  138. front_cnt = 0
  139. total_cnt = 0
  140. for d in data:
  141. if front_set.issubset(d):
  142. front_cnt += 1
  143. if total_set.issubset(d):
  144. total_cnt += 1
  145. return total_cnt / front_cnt
  146. if __name__ == '__main__':
  147. # 记录开始时间
  148. s = time.time()
  149. # 数据集
  150. data = [
  151. [1, 3, 4],
  152. [2, 3, 5],
  153. [1, 2, 3, 5],
  154. [2, 5]
  155. ]
  156. # 获取项的字典
  157. item_set = get_item_set(data)
  158. print("项的字典:", item_set)
  159. # 根据 Apriori算法 获取 n-频繁项集
  160. infrequent_list, frequent_list, frequent_support_list = apriori(item_set, data, min_support=0.50)
  161. # 生成规则
  162. rule_set = generate_rules(frequent_list, data, min_confidence=0.60)
  163. # 输出总用时
  164. print("总用时:", (time.time() - s), "s")

运行输出

  1. 项的字典: {1, 2, 3, 4, 5}
  2. 1-项集: [{1}, {2}, {3}, {5}] , 支持度分别为: [0.5, 0.75, 0.75, 0.75]
  3. 2-项集: [{1, 3}, {2, 3}, {2, 5}, {3, 5}] , 支持度分别为: [0.5, 0.5, 0.75, 0.5]
  4. 3-项集: [{2, 3, 5}] , 支持度分别为: [0.5]
  5. 4-项集: [] , 支持度分别为: []
  6. 5-项集: [] , 支持度分别为: []
  7. 规则1: {1} ==> {3} , confidence: 1.0
  8. 规则2: {3} ==> {1} , confidence: 0.6666666666666666
  9. 规则3: {2} ==> {3} , confidence: 0.6666666666666666
  10. 规则4: {3} ==> {2} , confidence: 0.6666666666666666
  11. 规则5: {2} ==> {5} , confidence: 1.0
  12. 规则6: {5} ==> {2} , confidence: 1.0
  13. 规则7: {3} ==> {5} , confidence: 0.6666666666666666
  14. 规则8: {5} ==> {3} , confidence: 0.6666666666666666
  15. 规则9: {2} ==> {3, 5} , confidence: 0.6666666666666666
  16. 规则10: {3} ==> {2, 5} , confidence: 0.6666666666666666
  17. 规则11: {5} ==> {2, 3} , confidence: 0.6666666666666666
  18. 规则12: {2, 3} ==> {5} , confidence: 1.0
  19. 规则13: {2, 5} ==> {3} , confidence: 0.6666666666666666
  20. 规则14: {3, 5} ==> {2} , confidence: 1.0
  21. 总用时: 0.0 s

五、使用 mlxtend 工具包得出频繁项集与规则

1、安装 mlxtend 工具包

pip install mlxtend

2、引入相关库

  1. import pandas as pd
  2. # 设置pandas输出表格的属性
  3. pd.options.display.max_colwidth=100
  4. pd.options.display.width=500
  5. from mlxtend.frequent_patterns import apriori, association_rules

3、自定义一份数据集

  1. # 自定义一份数据集
  2. data = {
  3. 'ID': [1, 2, 3, 4, 5, 6],
  4. 'Onion': [1, 0, 0, 1, 1, 1],
  5. 'Potato': [1, 1, 0, 1, 1, 1],
  6. 'Burger': [1, 1, 0, 0, 1, 1],
  7. 'Milk': [0, 1, 1, 1, 0, 1],
  8. 'Beer': [0, 0, 1, 0, 1, 0],
  9. }
  10. df = pd.DataFrame(data)
  11. print(df)

在这里插入图片描述

3.1 得到频繁项集

  1. # 利用mlxtend提供的apriori算法函数得到频繁项集,其中设置最小支持度为50%
  2. frequent_item_sets = apriori(df[['Onion', 'Potato', 'Burger', 'Milk', 'Beer']], min_support=0.50, use_colnames=True)
  3. print(frequent_item_sets)

在这里插入图片描述

3.2 计算规则

  1. # 计算规则,并设置提升度阈值为 1 (返回的是各个指标的数值,可以按照按兴趣的指标排序观察,但具体解释还得参考实际数据的含义)
  2. rules = association_rules(frequent_item_sets, metric='lift', min_threshold=1)
  3. print(rules)

在这里插入图片描述

3.3 挑选有用的规则进行分析

print(rules[(rules['lift'] > 1.125) & (rules['confidence'] > 0.8)])

在这里插入图片描述

通过一些自定义条件,筛选出自己感兴趣的结果。如上,我们可以分析得

  • (洋葱和马铃薯)(汉堡和马铃薯)可以搭配着来卖
  • 如果洋葱和汉堡都在顾客的购物篮中,顾客购买马铃薯得可能性也较高,如果他篮子里没有,则可以推荐一下

4、数据集制作

实际场景中,我们拿到的数据往往如下图所示:

在这里插入图片描述
也就是说,我们的初始输出往往是每个顾客购买了哪些商品,都是字符串类型的,而并非像我们上一节用到的那种标准格式的。这一节就讲讲怎么讲上面的原始数据转化为我们需要的格式。

4.1 导入相关库 

  1. import pandas as pd
  2. # 设置pandas输出表格的属性
  3. pd.options.display.max_colwidth = 100
  4. pd.options.display.width = 500
  5. from mlxtend.frequent_patterns import apriori, association_rules

4.2 构建原始数据集 

  1. # 原始数据集
  2. data = {
  3. 'ID': [1, 2, 3, 4, 5, 6],
  4. 'Basket': [
  5. ['Beer', 'Diaper', 'Pretzels', 'Chips', 'Aspirin'],
  6. ['Diaper', 'Beer', 'Chips', 'Lotion', 'Juice', 'BabyFood', 'Milk'],
  7. ['Soda', 'Chips', 'Milk'],
  8. ['Soup', 'Beer', 'Diaper', 'Milk', 'IceCream'],
  9. ['Soda', 'Coffee', 'Milk', 'Bread'],
  10. ['Beer', 'Chips']
  11. ]
  12. }
  13. data = pd.DataFrame(data)
  14. print(data)

在这里插入图片描述

4.3 处理成标准格式 

  1. # 将 Basket 列取出来单独处理,然后再将处理好的数据拼接回去
  2. print(" ID列 ".center(100, '='))
  3. data_id = data.drop('Basket', 1)
  4. print(data_id)
  5. print(" Basket列 ".center(100, '='))
  6. basket = data.Basket
  7. print(basket)
  8. print(" 将列表转化为字符串的Basket列 ".center(100, '='))
  9. basket = data.Basket.str.join(',')
  10. print(basket)
  11. print(" 根据Basket列数据转化为数值型 ".center(100, '='))
  12. basket = basket.str.get_dummies(',')
  13. print(basket)
  14. print(" 将数值型数据拼接回原数据 ".center(100, '='))
  15. data = data_id.join(basket)
  16. print(data)

在这里插入图片描述

4.4 用标准数据继续关联规则分析的步骤 

  1. # 用标准数据继续关联规则分析的步骤
  2. # 利用mlxtend提供的apriori算法函数得到频繁项集,其中设置最小支持度为50%
  3. frequent_item_sets = apriori(data[['Aspirin', 'BabyFood', 'Beer', 'Bread', 'Chips', 'Coffee', 'Diaper', 'IceCream',
  4. 'Juice', 'Lotion', 'Milk', 'Pretzels', 'Soda', 'Soup']], min_support=0.50,
  5. use_colnames=True)
  6. print(frequent_item_sets)

在这里插入图片描述

4.5 计算规则 

  1. # 计算规则,并设置提升度阈值为 1 (返回的是各个指标的数值,可以按照按兴趣的指标排序观察,但具体解释还得参考实际数据的含义)
  2. rules = association_rules(frequent_item_sets, metric='lift', min_threshold=1)
  3. print(rules)

在这里插入图片描述

5、电影数据集关联分析

5.1 数据集获取

Index of /datasets/movielens

在这里插入图片描述

在这里插入图片描述

5.2 引入相关库

  1. import pandas as pd
  2. # 设置pandas输出表格的属性
  3. pd.options.display.max_colwidth = 100
  4. pd.options.display.width = 500
  5. from mlxtend.frequent_patterns import apriori, association_rules

5.3 读取数据集

  1. # 读取原始数据
  2. movies = pd.read_csv(
  3. r'E:\Software\JetBrainsIDEA\PythonIDEA\Projects\CXSJS\Python\机器学习\唐宇迪机器学习\关联规则\data\movies\movies.csv')
  4. movies.head(10)

在这里插入图片描述

5.4 标准化数据集

  1. # 第一步,当然是将原始数据转化为标准格式啦
  2. movies_standard = movies.drop('genres', 1).join(movies.genres.str.get_dummies())
  3. movies_standard.head(10)
  4. # 一共包含 9742 部电影,一共有20种不同的电影类型(有2列是ID和电影名)
  5. print(movies_standard.shape) # (9742, 22)

在这里插入图片描述

5.5 获取频繁项集

  1. # 利用mlxtend提供的apriori算法函数得到频繁项集,其中设置最小支持度为0.05
  2. movies_standard.set_index(['movieId', 'title'], inplace=True)
  3. frequent_item_sets = apriori(movies_standard, min_support=0.05, use_colnames=True)
  4. print(frequent_item_sets)

在这里插入图片描述

5.6 计算规则

  1. # 计算规则,并设置提升度阈值为 1.25 (返回的是各个指标的数值,可以按照按兴趣的指标排序观察,但具体解释还得参考实际数据的含义)
  2. rules = association_rules(frequent_item_sets, metric='lift', min_threshold=1.25)
  3. print(rules)

在这里插入图片描述

5.7 结果分析

  1. # 对lift降序排序,查看lift较大的是哪些规则
  2. rules_sort = rules.sort_values(by=['lift'], ascending=False)
  3. print(rules_sort)

在这里插入图片描述
由上图可知,Adventure(冒险) 和 Action(动作片) 两个类型是最相关的,这和常识相符。

六、总结

  ①Apriori算法的缺点:(1)由频繁k-1项集进行自连接生成的候选频繁k项集数量巨大。(2)在验证候选频繁k项集的时候需要对整个数据库进行扫描,非常耗时。

  ②网上提到的频集算法的几种优化方法:1. 基于划分的方法。2. 基于hash的方法。3. 基于采样的方法。4. 减少交易的个数。
 

数据挖掘系列(1)关联规则挖掘基本概念与Aprior算法 - CodeMeals - 博客园

Apriori算法原理总结 - 刘建平Pinard - 博客园

关联规则(Association Rules)学习_君子慎独_诚意的博客-CSDN博客_关联规则

【机器学习】关联规则挖掘算法 + 三大案例实战 + Apriori算法 + Python代码实现_WSKH0929的博客-CSDN博客

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/489907
推荐阅读
相关标签
  

闽ICP备14008679号