购物篮分析最初出现于大型零售商,他们通过分析大量的发票数据,分析出购买特定商品的消费者更可能还购买哪种商品。
Transactions数据集
每一个这样的数据,其实就是一个消费者一次购买的商品清单,我们将这样的一个数据称为transaction。这样,训练的数据大概是这样的,
ID | transaction |
---|---|
0 | 牛奶、面包、尿布 |
1 | 可乐、面包、尿布、啤酒 |
2 | 牛奶、尿布、啤酒、鸡蛋 |
3 | 面包、牛奶、尿布、啤酒 |
4 | 面包、牛奶、尿布、可乐 |
Apriori算法
下面,我们将以上面的这个数据为例,用apriori算法做关联分析,说明apriori算法的过程。
算法过程是这样的:基于支持度求频繁项集 → \rightarrow →基于可信度求关联规则
1. 频繁项集
- 我们假设支持度和可信度均为0.5,则5条transaction下,个数必须达到 transaction个数支持度=50.5=2.5 个以上,才能进入频繁项集
- 首先求频繁1项集,观察到,牛奶出现4次>2.5次,入选;面包出现4次>2.5次,入选;尿布出现5次>2.5次,入选;啤酒出现3次>2.5次,入选;可乐出现2次<2.5次,不入选;鸡蛋出现1次<2.5次,不入选。综上,我们有频繁1项集如下
1项集 | 出现次数 |
---|---|
牛奶 | 4 |
面包 | 4 |
尿布 | 5 |
啤酒 | 3 |
- 其次求频繁2项集,对频繁1项集的商品进行组合,比如{牛奶、面包},检查这种组合在原来数据中出现次数是否大于2.5次。详细的,我们有,{牛奶、面包}出现3次>2.5次,入选;{牛奶、尿布}出现4次>2.5次,入选;{牛奶、啤酒}出现2次<2.5次,不入选;{面包、尿布}出现4次>2.5次,入选;{面包、啤酒}出现2次<2.5次,不入选;{尿布、啤酒}出现3次>2.5次,入选。综上,频繁2项集为
2项集 | 出现次数 |
---|---|
{牛奶、面包} | 3 |
{牛奶、尿布} | 4 |
{面包、尿布} | 4 |
{尿布、啤酒} | 3 |
- 然后求频繁3项集,对频繁2项集的元素进行组合,有,{牛奶、面包、尿布}在transaction数据中出现3次>2.5次,入选;{牛奶、尿布、啤酒}出现2次<2.5次,不入选;{面包、尿布、啤酒}出现2次<2.5次,不入选。综上,频繁3项集为
3项集 | 出现次数 |
---|---|
{牛奶、面包、尿布} | 3 |
我们不用考虑频繁4项集了,因为频繁3项集中只有一个元素,没法做组合产生4项集元素。
2. 关联规则
然后,我们根据频繁项集,求出关联规则。
- 首先看频繁3项集,里面只有{牛奶、面包、尿布},这样我们就分别看 牛奶 → \rightarrow →{面包、尿布},计算 { 牛 奶 、 面 包 、 尿 布 } { 牛 奶 } = 3 4 > 0.5 = 可 信 度 \frac{\{牛奶、面包、尿布\}}{\{牛奶\}}=\frac{3}{4}>0.5=可信度 {牛奶}{牛奶、面包、尿布}=43>0.5=可信度,则将 牛奶 → \rightarrow →{面包、尿布} 这条规则纳入关联规则中,意思就是,当有人买牛奶时,可以推荐给这人面包和尿布;再看其他的组合,如 面包 → \rightarrow →{牛奶、尿布}、{尿布、牛奶} → \rightarrow →面包 等等。
- 然后再看频繁2项集,举个例子,从2频繁集{牛奶、面包}中,能提取出规则 牛奶 → \rightarrow →面包 和 面包 → \rightarrow →牛奶,接下来需要验证这两条规则是否能满足可信度要求。对于牛奶 → \rightarrow →面包 而言,计算 { 牛 奶 、 面 包 } { 牛 奶 } = 3 4 > 0.5 = 可 信 度 \frac{\{牛奶、面包\}}{\{牛奶\}}=\frac{3}{4}>0.5=可信度 {牛奶}{牛奶、面包}=43>0.5=可信度,因此可以纳入关联规则。
简单实战
这里,我们用movielens的ratings数据集,采用Apriori算法,做出topN推荐。
movielens数据集已经上传,各位可以免费下载。
我们的想法是这样的,
- 对于每一个用户,把他看过的电影作为一个transaction
- 对这样的transaction数据集进行关联分析
- 根据生成的关联规则,通过对用户看过的电影进行关联,得到推荐的电影
# 第三方库
import pandas as pd
import numpy as np
from efficient_apriori import apriori
# 载入数据
data = pd.read_csv(r'D:\myfile\开课吧\推荐系统\第三节\movielens\ratings.csv')
data.head()
# 创建user_items字典,记录用户u看过的电影i的集合
user_items = {}
# 创建 transaction 数据集
transactions = []
for user, group in data.groupby('userId')['movieId']:
user_items[user] = set(group.values.tolist())
transactions.append(tuple(group.values.tolist()))
# 用apriori算法训练,得到频繁项集itemsets 和 关联规则rules
itemsets, rules = apriori(transactions, min_support=0.05, min_confidence=0.6)
# 选出关联规则中形式如 单个物品->物品 的规则
# 将这样的规则重写为字典形式,{单个物品:物品}
# 这样的字典为 rule_recommend
rule_recommend = {}
for rule in rules:
# 选出 单个物品->物品 的规则
if len(rule.lhs) == 1:
rule_recommend[rule.lhs[0]] = set(rule.rhs)
# 我们仅用 物品->物品 的方式进行推荐
# 而不用 {物品1,物品2}->物品 的方式进行推荐
def topN(u):
# 推荐列表
recommend = set()
# 用过的物品
used_items = user_items[u]
for item in used_items:
if item in rule_recommend.keys():
recommend.union(rule_recommend[item])
# 去掉用过的物品
res = recommend.difference(set(used_items))
return res
这里存在两个问题,
- 运行时间很长
- 预测效果不佳
由于apriori算法组合数次地遍历整个数据集,导致运行时间很长,这是可以理解的,我们可以用fpgrowth算法作为替代。
但是,对于预测效果不佳这个事情,可能的原因有两点,第一,关联规则运用不彻底,实际上,我们仅考虑单个物品对于其他物品的关联,并没有将物品组合对其他物品的关联也纳入推荐机制中;第二,可能是参数不合适,通过调参,或许能有所改善。