1、Apriori算法介绍
Apriori算法是Agrawal和R.Srikant于1994年提出的,为布尔关联规则挖掘频繁项集的原创性算法。算法的名字基于这样的事实:算法使用频繁项集性质的先验知识。
Apriori算法使用一种称为逐层搜索的迭代算法,其中k项集用于探索(k+1)项集。首先,通过扫描数据库,累计每个项的计数,并收集满足最小支持度的项,找出频繁1项集的集合,该集合记为L1。然后,使用L1找出频繁2项集的集合L2,使用L2找出L3,如此下去,直到不能再找到频繁k-项集。找出每个Lk需要一次数据库的完整扫描。
为了提高频繁项集逐层产生的效率,一种称为先验性质的重要性质用于压缩搜索空间。先验性质:频繁项集的所有非空子集也一定是频繁的。
2、连接步和剪枝步
“如何在算法中使用先验性质?”为理解这一点,我们考察如何使用Lk-1找出Lk,其中k>=2。下面的两步过程由连接步和剪枝步组成。
(1)连接步:为找出Lk,通过将Lk-1与自身连接产生候选k项集的集合。该候选项集的集合记为Ck。设l1和l2是Lk-1的项集,为了有效的实现,Apriori算法假定事务或项集中的项按字典序排序。两个项集连接的条件:前k-2项相同,第一个项集的k-1项小于第一个项集的k-1项(为了确保不产生重复)。
(2)剪枝步:为了压缩Ck,可以使用先验性质。任何非频繁的(k-1)项集都不是频繁k项集的子集。注意,由于Apriori算法使用逐层搜索技术,给定候选k项集后,只需检查它们的(k-1)项所有子集是否频繁。
3、Apriori算法的例子
上图为某商场的交易记录,共有9个事务,利用Apriori算法寻找所有的频繁项集的过程如下(最小支持度计数为2):
该过程的中间步骤是递归形式:先自连接、再剪枝、最后再支持度计数。
4、Apriori算法伪代码
5、由频繁项集产生关联规则
一旦由数据库D中的事务找出频繁项集,就可以直接由它们产生强关联规则。对于置信度,可以用下式计算:
Confidence(A=>B)=P(B|A)=support_count(AB)/support_count(A)
关联规则产生步骤如下:
1)对于每个频繁项集l,产生其所有非空真子集;
2)对于每个非空真子集s,如果support_count(l)/support_count(s)>=min_conf,则输出规则s=>(l-s),其中,min_conf是最小置信度阈值。
例如,在上述例子中,针对频繁集{I1,I2,I5}。可以产生哪些关联规则?该频繁集的非空真子集有{I1,I2},{I1,I5},{I2,I5},{I1},{I2}和{I5},对应置信度如下:
{I1,I2}=>I5
{I1,I5}=>I2
{I2,I5}=>I1
I1=>{I2,I5}
I2=>{I1,I5}
I5=>{I1,I2}
如果min_conf=70%,则强规则有{I1,I5}=>I2,{I2,I5}=>I1,I5=>{I1,I2}。
6、算法的python实现
6.1 使用Apriori算法发现频繁项集
from numpy import *
def loadDataSet(): # 测试数据集
return [[1,2,5],[2,4],[2,3],[1,2,4],[1,3],[2,3],[1,3],[1,2,3,5],[1,2,3]]
def createC1(dataSet): # 构建集合C1
C1 = []
for transaction in dataSet:
for item in transaction:
if not [item] in C1:
C1.append([item])
C1.sort() # C1中的元素进行排序,生成[[1],[2],[3],[4],[5]]
return map(frozenset, C1) # 把每个单元列表映射到frozenset(), frozenset是指被“冰冻”的
# 集合,就是说它们是不可改变的,这里不能用set,因为之后必须要将
# 这些集合作为字典键值使用。
def scanD(D, Ck, minSupport): # 用于从Ck生成Lk,并返回一个包含支持度值的字典supportData以备后用
ssCnt = {}
for tid in D:
for can in Ck:
if can.issubset(tid): # 计数
if not ssCnt.has_key(can): ssCnt[can]=1
else: ssCnt[can] += 1
numItems = float(len(D))
retList = []
supportData = {}
for key in ssCnt:
support = ssCnt[key]/numItems
if support >= minSupport:
retList.insert(0,key) # 列表的首位置插入字典的关键字
supportData[key] = support
return retList, supportData
def aprioriGen(Lk, k): # 其实这里是用Lk-1生成Ck,但这里的Lk-1都直接使用Lk来代替了
retList = []
lenLk = len(Lk)
for i in range(lenLk):
for j in range(i+1, lenLk):
L1 = list(Lk[i])[:k-2]; L2 = list(Lk[j])[:k-2]
L1.sort(); L2.sort()
if L1==L2: # 前k-2个项相同时,将两个集合Lk[i]和Lk[j]合并
retList.append(Lk[i] | Lk[j])
return retList
def apriori(dataSet, minSupport = 0.22): # 主函数
C1 = createC1(dataSet)
D = map(set, dataSet)
L1, supportData = scanD(D, C1, minSupport)
L = [L1]
k = 2 # 先从L1构建C2开始
while (len(L[k-2]) > 0):
Ck = aprioriGen(L[k-2], k)
Lk, supK = scanD(D, Ck, minSupport) # scan DB to get Lk
supportData.update(supK) # 更新supportData
L.append(Lk)
k += 1
return L, supportData
运行结果:
>>> ================================ RESTART ================================
>>>
>>> dataset=loadDataSet()
>>> dataset
[[1, 2, 5], [2, 4], [2, 3], [1, 2, 4], [1, 3], [2, 3], [1, 3], [1, 2, 3, 5], [1, 2, 3]]
>>> L, supportData=apriori(dataset)
>>> L
[[frozenset([1]), frozenset([3]), frozenset([2]), frozenset([5]), frozenset([4])], [frozenset([1, 3]), frozenset([2, 5]), frozenset([2, 3]), frozenset([1, 5]), frozenset([1, 2]), frozenset([2, 4])], [frozenset([1, 2, 5]), frozenset([1, 2, 3])], []]
>>> L[0] # 频繁1项集
[frozenset([1]), frozenset([3]), frozenset([2]), frozenset([5]), frozenset([4])]
>>> L[1] # 频繁2项集
[frozenset([1, 3]), frozenset([2, 5]), frozenset([2, 3]), frozenset([1, 5]), frozenset([1, 2]), frozenset([2, 4])]
>>> L[2] # 频繁3项集
[frozenset([1, 2, 5]), frozenset([1, 2, 3])]
>>> L[3] # 频繁4项集
[]
6.2 从频繁项集中挖掘关联规则