在上篇文章《数据挖掘之Apriori频繁项集挖掘》中我们用代码手工实现了Apriori算法, 用《数据挖掘概念与技术》中的数据做检验,和书中结果一致。本篇文章, 我们基于一个更大数据集,使用Apriori算法做关于购物篮分析的频繁项集挖掘和关联规则的生成。另外本文在频繁项集挖掘和关联规则生成时,同时使用调包和手工两种方法,以检验手工实现的结果是否正确,并了解学习现成包的用法,后续实际场景使用时肯定是直接调包,不太可能手动将过程全部写一遍。

本文是对《数据挖掘概念与技术》第六章内容的通俗理解和代码实现,更详细的理论知识请参考书中内容, 本文涉及的数据集和完整jupyter代码, 可以在我们的 "数据臭皮匠" 中输入"第六章2" 拿到

一、Apriori频繁项集挖掘

1.数据集介绍:

本文使用到的数据集只有一个文件 【purchase.csv】, 有53704行,13列.但使用到的只有两列,"交易号"和"商品大类" , 所以我们的目标是看, 购买了一种大类的商品后是否会继续购买其他大类的商品。其中:

① 商品编号代表具体的商品,每个商品都有自己的归类——商品小类和商品大类,商品大类包含商品小类;例如,商品编号为40165961是一款“28x28 厘米灰色煎锅”,其商品小类为141,即Cookware,其商品大类为14,即Cooking

② 表purchase.csv每一行为一个顾客(主卡)购买一种商品(商品编号)的交易记录;

③ POS.ID为店内POS收银机的编号

④ 主卡为会员卡号

⑤ 交易号为一笔交易的唯一编号

⑥ 小票序号该商品在小票上的序号(一个交易号可以对应多个小票序号)

⑦ 其他字段意义明确,不做赘述。

2.手工实现Apriori频繁项集挖掘

刚才说过,之前的文章中我们手工实现了Apriori算法, 但为保持与书中结论一致, 最小支持度使用的是绝对数字, 实际使用中根据相对支持度判断频繁项集的方法更常用, 本文对上篇文章的代码做了优化, 使函数支持相对支持度, 并且为与mlxtend.frequent_pattern.apriori 包的结果展示形式一致, 对结果展示做了加工,具体代码如下

def aprioriGen(Lk_1, k): #creates Ck """ 根据k-1项集产生候选k项集 Lk_1: k-1项集的列表 k: k项集的k """     retList = [] lenLk_1 = len(Lk_1)  # k-1项集的长度  for i in range(lenLk_1): for j in range(i+1, lenLk_1):  L1 = sorted(list(Lk_1[i]))   # k等于2时, 1项集取空, k等于3时,2项集取第一个元素, k等于4时,3项集取前两个元素 L2 = sorted(list(Lk_1[j]))   # k等于2时, 1项集取空, k等于3时,2项集取第一个元素, k等于4时,3项集取前两个元素 if L1[:k-2]==L2[:k-2]: # 如果前k减2个元素相等 , 就将两几何求并  retList.append(Lk_1[i] | Lk_1[j]) #set union return retList def get_subset(ss): """求一个k项集的所有k-1项集子集""" ls = list(ss) res = []  for i in range(len(ls)): res.append(set(ls[:i] + ls[(i+1):])) return res  def check_in(Lk_1,ck): """ 返回布尔值,  检验候选k项集的所有k-1项集子集 是否都在L_(k-1)中 """ i = 0  # 取出候选k项集的一个k-1项集子集 for ss_sub in get_subset(ck): # 如果该k-1项集子集在在L_(k-1)中, 加1  if ss_sub in Lk_1: i+= 1  # 返回候选k项集的所有k-1项集子集 是否都在L_(k-1)中 return i == len(ck) def cut_branch(Ck,Lk_1): """剪枝,  只保留候选K项集集合中, 所有k-1项集都在L_(k-1) 的候选k项集 """ Ck_res = []  # 取出一个候选k项集 for ck in Ck:  # 判断候选k项集的所有k-1项集子集 是否都在L_(k-1)中 flag = check_in(Lk_1,ck) # 如果是, 保留该候选k项集 if flag :  Ck_res.append(ck)  return Ck_res  def createC1(dataSet): """从数据集中构建一项集候选集,  将每个一项集都以frozenset的数据类型保存 因为frozenset可以作为字典的key, 常规的set不可以, 方便后续对候选项集计数 """ C1 = [] for transaction in dataSet: for item in transaction: if not [item] in C1: C1.append([item]) # frozenset 可以作为字典的key, set 不可以  return [frozenset(i) for i in C1] def scanD(D, Ck, minSupport):         """ D:数据集 Ck:候选k项集 minSupport:最小支持度阈值 """ # 扫描数据集,确定Ck中每个候选的计数 ssCnt = {} for tid in D: for can in Ck: if can.issubset(tid): ssCnt[can] = ssCnt.get(can,0)+1 # 根据最小支持度阈值筛选出频繁k项集  retList = [] supportData = {} for key in ssCnt: # 计算备选k项集的支持度 if (minSupport>0)&(minSupport<=1) : support = ssCnt[key]/len(D) else: support = ssCnt[key] # 如果支持度大于阈值, insert进k项集的结果列表 if support >= minSupport: retList.insert(0,key) # 不管支持度是否大于阈值, 都记录下该备选k项集的支持度  supportData[key] = support return retList, supportData def apriori_manul(dataSet, minSupport): C1 = createC1(dataSet)
#     print('C1:',C1) D = [set(i) for i in dataSet] # 检查C1中每个备选1项集的支持度, L1为筛选出的1项集, supportData为每个备选1项集的支持度 L1, supportData = scanD(D, C1, minSupport)
#     print(f'L1:{L1}','\n') L = [L1] # 将1项集列表插入频繁k项集的结果列表 k = 2  # 2项集     # k项集为空的时候停止 while (len(L[k-2]) > 0):     Ck = aprioriGen(L[k-2], k)  # 连接步,产生备选k项集
#         print('过滤前:',len(Ck),Ck,'\n') Ck = cut_branch(Ck,L[k-2])  # 剪枝
#         print('过滤后:',len(Ck),Ck,'\n') Lk, supK = scanD(D, Ck, minSupport)#scan DB to get Lk  扫描数据集,确认C_k中的每个候选k项集是否为频繁项集
#         print('筛选后:',len(Lk),Lk,'\n') supportData.update(supK) L.append(Lk)  k += 1 return L, supportData def trans_data(df): """将数据转换成函数需要的样子, 有订单物品组成的元组为一行""" data_ls = []  for i in range(len(df)): # 拿出一行数据 ss = df.iloc[i,:] # 将该行中值为非空的列的名称拿出来, 即该笔订单买的商品大类的清单 ss2 = ss[ss!=0].index.tolist() data_ls.append(ss2) return data_ls  def trans_res(L,supportData): """将结果转换成跟mlxtend.frequent_patterns.apriori 相同的格式""" res = {} # 从supportData中将频繁项集的支持度拿出来for items in L: for item in items: res[item] = supportData[item] # 调整展示格式df_res = pd.DataFrame(res,index=[0]).T df_res.reset_index(inplace=True,drop=False) df_res.columns = ['itemsets','support'] df_res = df_res[['support','itemsets']] return df_res
# 读取数据
df = pd.read_csv('purchase.csv',encoding='gbk')
df['times'] = 1
df2 = df.pivot_table(index='交易号',columns='商品大类',values='times').fillna(0).astype('int') # 手写函数apriori_manul 的结果
data_ls = trans_data(df2)
L,supportData = apriori_manul(data_ls,minSupport=0.6)
df_res =trans_res(L,supportData)
df_res.sort_values('support',inplace=True)
df_res

3.调包实现Apriori的频繁项集挖掘

# 读取数据
df = pd.read_csv('purchase.csv',encoding='gbk')
df['times'] = 1
df2 = df.pivot_table(index='交易号',columns='商品大类',values='times').fillna(0).astype('int') # 调用mlxtend.frequent_patterns.apriori  结果
frequent_itemsets = apriori(df2, min_support=0.6, use_colnames=True, max_len=None)
frequent_itemsets.sort_values('support',inplace=True) 

4.对比结果

可以看到两者的结果完全一致, 均生成了61个频繁项集, 且对应频繁项集的支持度一致

二、根据频繁项集得到关联规则

1.理论部分

一旦找到频繁项集,根据频繁项集得到关联规则就只差简单的一步.规则只要满足最小支持度和最小置信度就称为强规则(即我们需要的结果)

Confidence(A==>B) = P(A|B) = support_count(A U B) / support_count(A)

其中, support_count(A U B)是同时包含项集A,B的行数, support_count(A) 是包含项集A的行数

所以关联规则产生只需两步:

  • 对于每个频繁项集l,产生l的所有非空子集

  • 对于l的每个非空子集s, 如果support_count(l) / support_count(s) >=min_conf , 则输出规则"s-->(l-s)" , 其中min_conf 是最小置信度阈值。(书中的公式为support_count(t) / support_count(s) 应该是有错误)

由于规则从频繁项集产生, 因此每个规则都自动的满足最小支持度,这里只要再计算是否大于最小阈值,即可知道能否产生新的关联规则。

2.手工实现挖掘关联规则

# 找出频繁项集的所有子集  def find_sub_set(ss): """找出一个集合的所有非空真子集""" if len(ss)==1: return [ss,] res = []  # 找到ss的所有k-1项的子集合 ls_sub = get_subset(ss) res.extend(ls_sub) # 递归调用自身, 直到集合的长度为1  for s_sub in ls_sub: res_sub = find_sub_set(s_sub) # 将res中没有的集合append进结果列表 res.extend([i for i in res_sub if i not in res]) return res def gene_rules(ss,supportData,min_conf): """由频繁项集生成关联规则""" res = []  # 如果是频繁1项集, 直接返回空listif len(ss)==1: return res # 找到该频繁项集的所有非空子集sub_sets =  find_sub_set(ss) # 将集合转换为frozenset, 因为frozenset可以作为字典的keysub_sets = [frozenset(i) for i in sub_sets] for sub_set in sub_sets: # 计算subset -->(ss-subset) 的置信度support_ss = supportData.get(ss,0) suport_subset = supportData.get(sub_set,np.inf) confidence = support_ss/suport_subset  # 如果置信度大于最小支持度,就留下该条规则if confidence > min_conf: res.append([sub_set,ss-sub_set,confidence]) return res res = []
# 将df_res 中的频繁项集, 一个一个按出来,生成关联规则
for i in range(len(df_res)): ss = df_res.iloc[i,1] rules = gene_rules(ss,supportData,min_conf=0.5) res.extend(rules) df_rules = pd.DataFrame(res,columns=['antecedents','consequents','confidence'])

3.调包实现挖掘关联规则

association_rule = association_rules( frequent_itemsets, metric='confidence', min_threshold=0.5)
association_rule.sort_values( by='confidence', ascending=False, inplace=True)    

4.对比结果

从结果可以看到, 手动实现的代码和调包结果完全一致, 均产生了234条关联规则, 且有(11,14) 产生的3条关联规则置信度结果一致。

三、使用书中数据挖掘关联规则

构造书中数据后, 直接调用上文已定义的函数, 产生关联规则, 可以看到, 结果与书中完全一致

# 构造书中数据
data_ls =[['I1','I2','I5'], ['I2','I4'], ['I2','I3'], ['I1','I2','I4'], ['I1','I3'], ['I2','I3'], ['I1','I3'], ['I1','I2','I3','I5'], ['I1','I2','I3']] # 产生频繁项集和每个项集的支持度
L, supportData =apriori_manul(data_ls,minSupport = 2)
df_res =trans_res(L,supportData) # 根据频繁项集得到关联规则
res = []
for i in range(len(df_res)): ss = df_res.iloc[i,1] rules = gene_rules(ss,supportData,min_conf=0.1) res.extend(rules) df_rules = pd.DataFrame(res,columns=['antecedents','consequents','confidence'])
df_rules.sort_values('antecedents')

四、Ending

从上文可以看到, 调包实现的代码量非常少,且能够完美实现Apriori算法, 后续实际应用时笔者更可能直接调包实现, 但笔者认为,如果能自己实现一遍Apriori,是非常有意义的,它让我们更加了解技术细节,同时更能总结出算法的优缺点。

更多精彩内容,请关注:数据臭皮匠 共中号

参考资料:

数据和分析思路来自文章: 《机器学习|关联规则与购物篮分析实战》

算法原理来自: 《数据挖掘概念与技术》 第六章

作者:范小匠

审核:灰灰匠

编辑:森匠

频繁项集挖掘实战和关联规则产生相关推荐

  1. 频繁项集挖掘算法——Apriori算法

    前言 关联规则就是在给定训练项集上频繁出现的项集与项集之间的一种紧密的联系.其中"频繁"是由人为设定的一个阈值即支持度 (support)来衡量,"紧密"也是由 ...

  2. 基于关联规则(Variational Autoencoders)疾病预测系统实战:(pyspark FPGrowth实现频繁项集挖掘、最后给出预测模型topK准确率和召回率)

    基于关联规则(Variational Autoencoders)疾病预测系统实战:(pyspark FPGrowth实现频繁项集挖掘.最后给出预测模型topK准确率和召回率) 目录

  3. 频繁项集挖掘之apriori和fp-growth

    Apriori和fp-growth是频繁项集(frequent itemset mining)挖掘中的两个经典算法,主要的区别在于一个是广度优先的方式,另一个是深度优先的方式,后一种是基于前一种效率较 ...

  4. 频繁项集挖掘算法在告警关联中的应用

    # 技术黑板报 # 第十期 推荐阅读时长:15min 在上一篇技术黑板报中,我们介绍了频繁项集挖掘这一问题,并讲解了Apriori算法与FP-Growth算法的技术原理.本期技术黑板报我们将主要围绕频 ...

  5. 频繁项集挖掘之Aprior和FPGrowth算法

    频繁项集挖掘的应用多出现于购物篮分析,现介绍两种频繁项集的挖掘算法Aprior和FPGrowth,用以发现购物篮中出现频率较高的购物组合. 基础知识 项:"属性-值"对.比如啤酒2 ...

  6. 数据频繁项集挖掘算法

    ** Apriori算法 ** Apriori 采用广度优先的搜索方式,缩小搜索空间用到了一个称为apriori的性质,其性质为:频繁项集的所有非空子集必然也是频繁的.这是很显然的,比如 同时包含项A ...

  7. 数据挖掘---频繁项集挖掘Apriori算法的C++实现

    1 准备   首先实现这个算法是基于中南大学软件学院数据挖掘课的上机作业.作业(全英文)下载地址:http://download.csdn.net/detail/freeape/9188451 2 作 ...

  8. 数据挖掘之Apriori频繁项集挖掘

    本文的代码文件原件可以在我们的 "数据臭皮匠" 中输入"第六章1" 拿到 1.基本概念介绍 频繁项集和关联规则的挖掘首先需要了解一些概念, 如支持度, 置信度, ...

  9. 关联分析/频繁项集挖掘:Apriori算法

    简介 Apriori是一种流行的算法,用于在关联规则学习中提取频繁项集.Apriori算法被设计用于对包含交易的数据库进行操作,例如商店客户的购买.如果项目集满足用户指定的支持阈值,则该项目集被视为& ...

最新文章

  1. 厦门试点医疗大数据 产值有望达1200亿
  2. elasticsearch外网访问
  3. html你可能还不知道的一些知识点
  4. Spring中拦截/和拦截/*的区别 - 不能访问到返回的JSP - 访问静态资源(jpg,js等)
  5. AI和IOT的结合:现在和未来
  6. slf4j log4j logback关系详解和相关用法
  7. C# 通过窗口句柄为指定窗口发送指令(user32.dll,mshtml)
  8. 网络邻居上不显示计算机,网上邻居不显示或找不到自己电脑怎么办?
  9. 算1 - n的阶乘和末6位(超详细)
  10. 英语思维导图大全 阅读理解(二十一)
  11. microsoft store 安装包_Microsoft 办公软件免费了?
  12. 批量下载sra文件linux,Linux下从NCBI批量下载SRA数据的sra和aspera方法
  13. 联想Y450 MAC系统SD读卡器驱动
  14. html插入swf自动播放,[转载]网页中插入FLASH(swf文件)的html代码
  15. 四川电信天邑TY1208-Z_强制刷机固件
  16. leetcode-从双倍数组中还原原数组
  17. xshell修改终端字符编码
  18. 磁盘阵列raid LVM创建
  19. 通过STM32单片机计算并控制小车速度,通过控制速度的思想扩展到控制其它变化量
  20. python实战——XML转Json

热门文章

  1. 【Linux系统编程】socket介绍
  2. JAVA学习路线资料
  3. Linux硬链接与软连接实现对比
  4. 没有天生的英雄,也没人生来平凡,是我们自己成就了自己。--夏目漱石
  5. 解决texstudio的matrix环境Missing $ inserted、 Misplaced alignment tab character 、Missing { inserted.\dot
  6. CSS3新特性详解(二):CSS3 字体@font-face详解、如何创建和修改woff字体文件及text-shadow等文本效果
  7. WPF PrismDialog PopupWindowAction使用MetroWindow
  8. C语言伪随机序列生成函数Pseudo-random sequence generation functions
  9. opencv滤镜-单色滤镜
  10. xxxxx.com 将您重定向次数过多