最近上数据挖掘的课程,其中学习到了频繁模式挖掘这一章,这章介绍了三种算法,Apriori、FP-Growth和Eclat算法;由于对于不同的数据来说,这三种算法的表现不同,所以我们本次就对这三种算法在不同情况下的效率进行对比。从而得出适合相应算法的情况。
GitHub:https://github.com/loyalzc/freqpattern
(一)算法原理
其中相应的算法原理在之前的博客中都有非常详细的介绍,这里就不再赘述,这里给出三种算法大概的介绍
但是这里给出每个算法的关键点:
1.1 Apriori算法:
- 限制候选产生发现频繁项集
- 重要性质:频繁项集所有非空子集也一定是频繁的。
- 主要步骤:
- 连接
- 剪枝
- 特点:需要多次扫描数据库,对于大规模数据效率很低!
Apriori算法原理详细介绍:http://www.cnblogs.com/90zeng/p/apriori.html
1.2 FP-Growth算法
- 通过模式增长挖掘频繁模式
- 主要步骤:
- 构建频繁模式树
- 构造条件模式基
- 挖掘频繁模式
- 特点:两次扫描数据库,采用分治的策略有效降低搜索开销
FP-Growth算法原理详细介绍:http://www.cnblogs.com/datahunter/p/3903413.html
1.3 Eclat算法
- 使用垂直格式挖掘频繁项集
- 主要步骤:
- 将数据倒排{ item:TID_set }
- 通过求频繁k项集的交集来获取k+1项集
- 特点:仅需要一次扫描数据库,TID集合很长的话需要消耗大量的内存和计算时间
Eclat算法原理详细介绍:http://www.cnblogs.com/catkins/p/5270484.html
(二)算法实现
由于各个博客给出的算法实现并不统一,而且本人在实现《机器学习实战》中FP-Growth算法的时候发现,在在创建FP-Tree时根据headTable中元素的支持度顺序的排序过程中,这个地方的排序方法写的有问题,当在模式稠密时,具有很多支持度相同的项集,书中的代码并没有考虑着一点,所以如果遇到支持度相同的项集那个就会出现一定的随机性,导致建树过程出错,最后的频繁项集结果会偏小,因此这里对改错误进行了纠正,在支持度相同时,添加了按照项集排序的规则,这样建立的FP-Tree才完全正确。
1.1 Apriori算法实现:
1 # -*- coding: utf-8 -*- 2 ''' 3 @author: Infaraway 4 @time: 2017/4/15 12:54 5 @Function: 6 ''' 7 8 9 def init_c1(data_set_dict, min_support): 10 c1 = [] 11 freq_dic = {} 12 for trans in data_set_dict: 13 for item in trans: 14 freq_dic[item] = freq_dic.get(item, 0) + data_set_dict[trans] 15 # 优化初始的集合,使不满足最小支持度的直接排除 16 c1 = [[k] for (k, v) in freq_dic.iteritems() if v >= min_support] 17 c1.sort() 18 return map(frozenset, c1) 19 20 21 def scan_data(data_set, ck, min_support, freq_items): 22 """ 23 计算Ck中的项在数据集合中的支持度,剪枝过程 24 :param data_set: 25 :param ck: 26 :param min_support: 最小支持度 27 :param freq_items: 存储满足支持度的频繁项集 28 :return: 29 """ 30 ss_cnt = {} 31 # 每次遍历全体数据集 32 for trans in data_set: 33 for item in ck: 34 # 对每一个候选项集, 检查是否是 term中的一部分(子集),即候选项能否得到支持 35 if item.issubset(trans): 36 ss_cnt[item] = ss_cnt.get(item, 0) + 1 37 ret_list = [] 38 for key in ss_cnt: 39 support = ss_cnt[key] # 每个项的支持度 40 if support >= min_support: 41 ret_list.insert(0, key) # 将满足最小支持度的项存入集合 42 freq_items[key] = support # 43 return ret_list 44 45 46 def apriori_gen(lk, k): 47 """ 48 由Lk的频繁项集生成新的候选项集 连接过程 49 :param lk: 频繁项集集合 50 :param k: k 表示集合中所含的元素个数 51 :return: 候选项集集合 52 """ 53 ret_list = [] 54 for i in range(len(lk)): 55 for j in range(i+1, len(lk)): 56 l1 = list(lk[i])[:k-2] 57 l2 = list(lk[j])[:k-2] 58 l1.sort() 59 l2.sort() 60 if l1 == l2: 61 ret_list.append(lk[i] | lk[j]) # 求并集 62 # retList.sort() 63 return ret_list 64 65 66 def apriori_zc(data_set, data_set_dict, min_support=5): 67 """ 68 Apriori算法过程 69 :param data_set: 数据集 70 :param min_support: 最小支持度,默认值 0.5 71 :return: 72 """ 73 c1 = init_c1(data_set_dict, min_support) 74 data = map(set, data_set) # 将dataSet集合化,以满足scanD的格式要求 75 freq_items = {} 76 l1 = scan_data(data, c1, min_support, freq_items) # 构建初始的频繁项集 77 l = [l1] 78 # 最初的L1中的每个项集含有一个元素,新生成的项集应该含有2个元素,所以 k=2 79 k = 2 80 while len(l[k - 2]) > 0: 81 ck = apriori_gen(l[k - 2], k) 82 lk = scan_data(data, ck, min_support, freq_items) 83 l.append(lk) 84 k += 1 # 新生成的项集中的元素个数应不断增加 85 return freq_items