《数据仓库与数据挖掘》课程论文
题目: 关联分析Apriori算法的研究和案例实现
专业: 计算机科学与技术
学号: 1606915078
姓名: 李勇智
2018-2019学年第二学期
目录
1.研究现状
1.1算法简介
Apriori算法是经典的挖掘频繁项集和关联规则的数据挖掘算法。A priori在拉丁语中指"来自以前"。当定义问题时,通常会使用先验知识或者假设,这被称作"一个先验"(a priori)。Apriori算法的名字正是基于这样的事实:算法使用频繁项集性质的先验性质,即频繁项集的所有非空子集也一定是频繁的。Apriori算法使用一种称为逐层搜索的迭代方法,其中k项集用于探索(k+1)项集。首先,通过扫描数据库,累计每个项的计数,并收集满足最小支持度的项,找出频繁1项集的集合。该集合记为L1。然后,使用L1找出频繁2项集的集合L2,使用L2找出L3,如此下去,直到不能再找到频繁k项集。每找出一个Lk需要一次数据库的完整扫描。Apriori算法使用频繁项集的先验性质来压缩搜索空间。
1.2研究现状
Apriori是Agrawal等于1993年设计的一个基本算法,首先提出了挖掘顾客交易数据库中项集间的关联规则问题,其核心方法是基于频繁项集理论的递推方法。算法提出后以后,很多研究人员对关联规则挖掘算法进行了大量研究,特别是对关联规则挖掘算法进行了大量的研究和优化。如Savasere等人设计了一个基于划分的算法,Mannila提出的基于采样的方法等。
近几年,随着研究者对于关联规则挖掘的深入研究,关联规则挖掘研究有了许多扩展,包括:事务间关联规则挖掘、空间关联规则挖掘、负关联规则挖掘、序列模式挖掘及正关联规则挖掘;另外,对度量的研究也有所突破。自从Chen,Hart,Brin和Motwani等人提出了强关联规则的兴趣度问题以后,强关联规则兴趣度问题的研究得到了很高的重视。后来评估关联规则兴趣度相应的一些优化方法的出现,促进了目前度量方法的进一步改进和完善。同时,Motwani和Silverstein把这方面的研究和讨论推广到相关的算法中。除这个方面外,其他方面的研究也得到了发展,如多层关联规则挖掘、区间数据关联规则挖掘、删除冗余规则、度量的改进研究、关联规则的有效增量更新和并行分布规则挖掘等等,这些关联规则挖掘算法在实际数据挖掘系统中的到了很好的应用。
2.算法思想
2.1相关概念
(1)项(item): 项指的是具体的一件东西,比如在购物篮的例子中,你的购物篮里面的大米,被子,红酒等等商品都是项;
(2)项集(itemset): 顾名思义,项的集合,由一个或多个项组成的一个整体,我们把由kk个项组成的项集叫kk项集;
(3)事务(transaction):一个事务,可以看做是发生的一次事件,比如一个人一次的购物清单,用符号TT表示,一般来说,TT包含一个它本身的身份——TIDTID以及事务中的一个项集。当然,如果我们收集很多这样的清单,构成一个数据库,这个数据库就能作为我们挖掘计算的依据了。这个库,用符号DD表示。
(4)关联规则:表示实体之间相关关系,用蕴含式A⇒BA⇒B表示A和B之间相关关系(A和B可以是两个项,也可以是两个项集),关联规则包含支持度(support)和置信度(confidence)两个层面指标;
(5)支持度(support):说的是所有事务中,A和B同时出现的次数与总的事务数的比例。换个说法,现实数据中,支持A和B这种关联的比例。用以下公式计算:
support(A⇒B)=P(A∪B)
其中,P(A∪B)的意思是事务中同时包含A和B的比例;
(6)置信度(confidence):A⇒BA⇒B的置信度说的是包含A的事务中,同时也包含B的事务所占的比例。用以下公式计算:
confidence(A⇒B)=P(B|A)
比如说,总共100个事务,有50个包含A,而这50个事务当中,又有20个同时也包含B,那么,confidence(A⇒B)=40%
了解了支持度(support)和置信度(confidence)两个概念,那么不妨可以再深入一步,拓展一个概念:绝对支持度;
支持度计数(绝对支持度):绝对支持度又叫支持度计数,频度或计数。上面我们定义的支持度其实也可以叫“相对支持度”,而绝对支持度则说的是一个实体出现的次数,比如:support(A) = A在全体事务数据库DD中出现的次数。这个概念很重要,因为依靠支持度计数我们就能通过support(A)support(A)和support(A∪B)support(A∪B)来计算置信度:
confidence(A⇒B)=P(B|A)=support(A∪B)support(A)
(7)强规则:我们将实体之间的关联规则定义为强规则,如果实体之间的相对支持度(support)和置信度(confidence)满足我们预定义的最小支持度阈值(min_sup)和最小置信度阈值(min_conf)。换句话说,只要我们在上面的概念5中定义的两个指标都满足,那么,实体之间就是极有强(关联)规则的;
(8)频繁项集:指的是频繁在事务中出现的项集,所谓“频繁”的标准就是这个项集出现的次数满足最小支持度计数(阈值)。
2.2 基本思想
该算法的基本思想是:首先找出所有的频集,这些项集出现的频繁性至少和预定义的最小支持度一样。然后由频集产生强关联规则,这些规则必须满足最小支持度和最小可信度。然后使用第1步找到的频集产生期望的规则,产生只包含集合的项的所有规则,其中每一条规则的右部只有一项,这里采用的是中规则的定义。一旦这些规则被生成,那么只有那些大于用户给定的最小可信度的规则才被留下来。为了生成所有频集,使用了递归的方法。
(1) L1 = find_frequent_1-itemsets(D);
(2) for (k=2;Lk-1 ≠Φ ;k++) {
(3) Ck = apriori_gen(Lk-1 ,min_sup);
(4) for each transaction t ∈ D {//scan D for counts
(5) Ct = subset(Ck,t);//get the subsets of t that are candidates
(6) for each candidate c ∈ Ct
(7) c.count++;
(8) }
(9) Lk ={c ∈ Ck|c.count≥min_sup}
(10) }
(11) return L= ∪ k Lk;
可能产生大量的候选集,以及可能需要重复扫描数据库,是Apriori算法的两大缺点。
3.算法步骤
下面我们对Aprior算法流程做一个总结。
输入:数据集合D,支持度阈值α
输出:最大的频繁k项集
(1)扫描整个数据集,得到所有出现过的数据,作为候选频繁1项集。k=1,频繁0项集为空集。
(2)挖掘频繁k项集
(a) 扫描数据计算候选频繁k项集的支持度
(b) 去除候选频繁k项集中支持度低于阈值的数据集,得到频繁k项集。如果得到的频繁k项集为空,则直接返回频繁k-1项集的集合作为算法结果,算法结束。如果得到的频繁k项集只有一项,则直接返回频繁k项集的集合作为算法结果,算法结束。
© 基于频繁k项集,连接生成候选频繁k+1项集。
(3) 令k=k+1,转入步骤2。
从算法的步骤可以看出,Aprior算法每轮迭代都要扫描数据集,因此在数据集很大,数据种类很多的时候,算法效率很低。
我们下面这个简单的例子看看:
图3.1.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, 125,135和235共4组,这部分图中没有画出。通过计算候选频繁3项集的支持度,我们发现123,125和135的支持度均为25%,因此接着被剪枝,最终得到的真正频繁3项集为235一组。由于此时我们无法再进行数据连接,进而得到候选频繁4项集,最终的结果即为频繁3三项集235。
4.算法应用
4.1所应用的数据集介绍
以超市交易为数据集,所有商品的项集为I = {bread, beer, cake, cream, milk, tea}
某条交易如Ti = {bread, beer, milk},可以简化为Ti = {a, b, d}
data.txt数据集样本如下
a, d, e,f
a, d, e
c, e
e, f
…
数据集位于本地的D:\data目录下,数据集一共有270行,前24行的数据内容如图4.1所示
图4.1.1
4.2核心代码
# -*- coding: utf-8 -*-
"""
Created on Tue Jun 25 21:39:58 2019
@author: lyz
"""
def load_data_set():
data_set = []
fd = open("d:\data\data.txt", "r")
for line in fd.readlines():
line = line.strip('\n')
data_set.append(line)
return data_set
'''
直接从数据集构造1-候选集
'''
def create_C1(data_set):
C1 = set()
for t in data_set:
for item in t:
item_set = frozenset([item])
C1.add(item_set)
return C1
'''
判断是否满足
'''
def is_apriori(Ck_item, Lksub1):
for item in Ck_item:
sub_Ck = Ck_item - frozenset([item])
if sub_Ck not in Lksub1:
return False
return True
'''
生成各个候选集Ck
'''
def create_Ck(Lksub1, k):
Ck = set()
len_Lksub1 = len(Lksub1)
list_Lksub1 = list(Lksub1)
for i in range(len_Lksub1):
for j in range(1, len_Lksub1):
l1 = list(list_Lksub1[i])
l2 = list(list_Lksub1[j])
l1.sort()
l2.sort()
if l1[0:k-2] == l2[0:k-2]:
Ck_item = list_Lksub1[i] | list_Lksub1[j]
if is_apriori(Ck_item, Lksub1):
Ck.add(Ck_item)
return Ck
'''
通过候选集Ck生成频繁集Lk
'''
def generate_Lk_by_Ck(data_set, Ck, min_support, support_data):
Lk = set()
item_count = {}
for t in data_set:
for item in Ck:
if item.issubset(t):
if item not in item_count:
item_count[item] = 1
else:
item_count[item] += 1
t_num = float(len(data_set))
for item in item_count:
if (item_count[item] / t_num) >= min_support:
Lk.add(item)
support_data[item] = item_count[item] / t_num
return Lk
'''
生成各阶频繁集,最小支持度为0.2
'''
def generate_L(data_set, k, min_support):
support_data = {}
C1 = create_C1(data_set)
L1 = generate_Lk_by_Ck(data_set, C1, min_support, support_data)
Lksub1 = L1.copy()
L = []
L.append(Lksub1)
for i in range(2, k+1):
Ci = create_Ck(Lksub1, i)
Li = generate_Lk_by_Ck(data_set, Ci, min_support, support_data)
Lksub1 = Li.copy()
L.append(Lksub1)
return L, support_data
'''
生成从频繁集关联规则分析
'''
def generate_big_rules(L, support_data, min_conf):
big_rule_list = []
sub_set_list = []
for i in range(0, len(L)):
for freq_set in L[i]:
for sub_set in sub_set_list:
if sub_set.issubset(freq_set):
conf = support_data[freq_set] / support_data[freq_set - sub_set]
big_rule = (freq_set - sub_set, sub_set, conf)
if conf >= min_conf and big_rule not in big_rule_list:
big_rule_list.append(big_rule)
sub_set_list.append(freq_set)
return big_rule_list
if __name__ == "__main__":
data_set = load_data_set()
L, support_data = generate_L(data_set, k=3, min_support=0.2)
big_rules_list = generate_big_rules(L, support_data, min_conf=0.7)
for Lk in L:
print ("=" * 50)
print ("frequent " + str(len(list(Lk)[0])) + "-itemsets\t\tsupport")
print ("=" * 50)
for freq_set in Lk:
print (freq_set, support_data[freq_set])
print()
print ("Big Rules")
for item in big_rules_list:
print (item[0], "=>", item[1], "conf: ", item[2])
4.3实现页面截图
图 4.3.1
图4.3.2
图4.3.3
图4.3.4
5.总结
Aprior算法是一个非常经典的频繁项集的挖掘算法,很多算法都是基于Aprior算法而产生的,包括FP-Tree,GSP, CBA等。这些算法利用了Aprior算法的思想,但是对算法做了改进,数据挖掘效率更好一些,因此现在一般很少直接用Aprior算法来挖掘数据了,但是理解Aprior算法是理解其它Aprior类算法的前提,同时算法本身也不复杂,因此值得好好研究一番。
这次的收获主要有以下几点:同一行数据,最小支持度越小,那么产生的频繁项集维数越高,程序运行的时间越长;频繁项集的子集一定是频繁的,子集频繁父亲一定频繁;Apriori也存在缺点:第一,在每一步产生候选项目集时循环产生的组合过多,没有排除不应该参与组合的元素;第二,每次计算项集的支持度时,开销会随着数据的增多而成几何级增长。