Apriori关联分析
关联规则挖掘可以让我们从数据集中发现项与项(item 与 item)之间的关系,它在我们的生活中有很多应用场景,“购物篮分析”就是一个常见的场景。
在今天的内容中,希望你能带着问题,和我一起来搞懂以下几个知识点:
- 搞懂关联规则中的几个重要概念:支持度、置信度、提升度;
- Apriori 算法的工作原理;
- 在实际工作中,我们该如何进行关联规则挖掘。
搞懂关联规则中的几个概念
我举一个超市购物的例子,下面是几名客户购买的商品列表:
什么是支持度呢?
支持度是个百分比,它指的是某个商品组合出现的次数与总次数之间的比例。支持度越高,代表这个组合出现的频率越大。
在这个例子中,我们能看到“牛奶”出现了 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 的出现概率提升的”程度。
Apriori 的工作原理
首先我们把上面案例中的商品用 ID 来代表,牛奶、面包、尿布、可乐、啤酒、鸡蛋的商品 ID 分别设置为 1-6,上面的数据表可以变为:
Apriori 算法其实就是查找频繁项集 (frequent itemset) 的过程,所以首先我们需要定义什么是频繁项集。
频繁项集就是支持度大于等于最小支持度 (Min Support) 阈值的项集,所以小于最小值支持度的项目就是非频繁项集,而大于等于最小支持度的项集就是频繁项集。
项集这个概念,英文叫做 itemset,它可以是单个的商品,也可以是商品的组合。我们再来看下这个例子,假设我随机指定最小支持度是 50%,也就是 0.5。
我们来看下 Apriori 算法是如何运算的。
首先,我们先计算单个商品的支持度,也就是得到 K=1 项的支持度:
因为最小支持度是 0.5,所以你能看到商品 4、6 是不符合最小支持度的,不属于频繁项集,于是经过筛选商品的频繁项集就变成:
在这个基础上,我们将商品两两组合,得到 k=2 项的支持度:
我们再筛掉小于最小值支持度的商品组合,可以得到:
以此类推,K = K+1,直至频繁项集为0
总结下 Apriori 算法的递归流程:
- K=1,计算 K 项集的支持度;
- 筛选掉小于最小支持度的项集;
- 如果项集为空,则对应 K-1 项集的结果为最终结果。
- 否则 K=K+1,重复 1-3 步。
Apriori 的改进算法:FP-Growth 算法
我们刚完成了 Apriori 算法的模拟,你能看到 Apriori 在计算的过程中有以下几个缺点:
- 可能产生大量的候选集。因为采用排列组合的方式,把可能的项集都组合出来了;
- 每次计算都需要重新扫描数据集,来计算每个项集的支持度。
所以 Apriori 算法会浪费很多计算空间和计算时间,为此人们提出了 FP-Growth 算法,它的特点是:
- 创建了一棵 FP 树来存储频繁项集。在创建前对不满足最小支持度的项进行删除,减少了存储空间。我稍后会讲解如何构造一棵 FP 树;
- 整个生成过程只遍历数据集 2 次,大大减少了计算量。
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。
条件模式基不包括“啤酒”节点,而且祖先节点如果小于最小支持度就会被剪枝,所以“啤酒”的条件模式基为空。
同理,我们可以求得“面包”的条件模式基为:
所以可以求得面包的频繁项集为{尿布,面包},{尿布,牛奶,面包}。同样,我们还可以求得牛奶,尿布的频繁项集,这里就不再计算展示。
总结:
如何使用 Apriori 工具包
Apriori 虽然是十大算法之一,不过在 sklearn工具包中并没有它,也没有 FP-Growth 算法。这里教你个方法,来选择 Python 中可以使用的工具包,你可以通过https://pypi.org/ 搜索工具包。
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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | from efficient_apriori import apriori # 设置数据集 data = [( '牛奶' , '面包' , '尿布' ), ( '可乐' , '面包' , '尿布' , '啤酒' ), ( '牛奶' , '尿布' , '啤酒' , '鸡蛋' ), ( '面包' , '牛奶' , '尿布' , '啤酒' ), ( '面包' , '牛奶' , '尿布' , '可乐' )] # 挖掘频繁项集和频繁规则 itemsets, rules = apriori(data, min_support = 0.5 , min_confidence = 1 ) print (itemsets) print (rules) # 运行结果: { 1 : {( '啤酒' ,): 3 , ( '尿布' ,): 5 , ( '牛奶' ,): 4 , ( '面包' ,): 4 }, 2 : {( '啤酒' , '尿布' ): 3 , ( '尿布' , '牛奶' ): 4 , ( '尿布' , '面包' ): 4 , ( '牛奶' , '面包' ): 3 }, 3 : {( '尿布' , '牛奶' , '面包' ): 3 }} [{啤酒} - > {尿布}, {牛奶} - > {尿布}, {面包} - > {尿布}, {牛奶, 面包} - > {尿布}] |
你能从代码中看出来,data 是个 List 数组类型,其中每个值都可以是一个集合。实际上你也可以把 data 数组中的每个值设置为 List 数组类型,比如:
1 2 3 4 5 | data = [[ '牛奶' , '面包' , '尿布' ], [ '可乐' , '面包' , '尿布' , '啤酒' ], [ '牛奶' , '尿布' , '啤酒' , '鸡蛋' ], [ '面包' , '牛奶' , '尿布' , '啤酒' ], [ '面包' , '牛奶' , '尿布' , '可乐' ]] |
两者的运行结果是一样的,efficient-apriori 工具包把每一条数据集里的项式都放到了一个集合中进行运算,并没有考虑它们之间的先后顺序。因为实际情况下,同一个购物篮中的物品也不需要考虑购买的先后顺序。而其他的 Apriori 算法可能会因为考虑了先后顺序,出现计算频繁项集结果不对的情况。所以这里采用的是 efficient-apriori 这个工具包。
挖掘导演是如何选择演员的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | # -*- coding: utf-8 -*- from efficient_apriori import apriori import csv director = u '宁浩' file_name = './' + director + '.csv' lists = csv.reader( open (file_name, 'r' , encoding = 'utf-8-sig' )) # 数据加载 data = [] for names in lists: name_new = [] for name in names: # 去掉演员数据中的空格 name_new.append(name.strip()) data.append(name_new[ 1 :]) # 挖掘频繁项集和关联规则 itemsets, rules = apriori(data, min_support = 0.5 , min_confidence = 1 ) print (itemsets) print (rules) |
1 2 3 4 | # 运行结果: { 1 : {( '徐峥' ,): 5 , ( '黄渤' ,): 6 }, 2 : {( '徐峥' , '黄渤' ): 5 }} [{徐峥} - > {黄渤}] |
Apriori 算法的核心就是理解频繁项集和关联规则。在算法运算的过程中,还要重点掌握对支持度、置信度和提升度的理解。在工具使用上,你可以使用 efficient-apriori 这个工具包,它会把每一条数据中的项(item)放到一个集合(篮子)里来处理,不考虑项(item)之间的先后顺序。
在实际运用中你还需要灵活处理,比如导演如何选择演员这个案例,虽然工具的使用会很方便,但重要的还是数据挖掘前的准备过程,也就是获取某个导演的电影数据集。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现