Apriori算法
Apriori算法定义
- 文字定义:
Apriori算法是第一个关联规则挖掘算法,也是最经典的算法。它利用逐层搜索的迭代方法找出数据库中项集的关系,以形成规则,其过程由连接(类矩阵运算)与剪枝(去掉那些没必要的中间结果)组成。该算法中项集的概念即为项的集合。包含K个项的集合为k项集。项集出现的频率是包含项集的事务数,称为项集的频率。如果某项集满足最小支持度,则称它为频繁项—百度文库 - 理解Apriori算法:
Apriori的原理,如果某个项集是频繁项集,那么它所有的子集也是频繁的。
以0,1,2,3为例子,有 2^4-1 种可能组合遍历。这是一种指数增长,而基于Apriori算法,可以有效降低计算的时间,提高计算效率。
假如 {0,1} 是频繁的,那么 {0}, {1} 也一定是频繁的。
反过来,若集合{2,3}是非频繁的,则{0,2,3},{1,2,3}和{0,1,2,3}也是非频繁的。
{0,1,2,3}共有2^4-1种组合:
{0},{1},{2},{3},
{0,1},{0,2},{0,3},{1,2},{1,3},{2,3}
{0,1,2},{0,1,3},{0,2,3},{1,2,3}
{0,1,2,3}
Apriori算法就是删除非频繁的项集,来达到降低计算的时间的效果。
藕粉色字为非频繁的项集,表示计算机可以忽略这些组合。
题目
- 数据库中有9个事务,即|D|=9。Apriori假定事务中的项按字典次序存放。
- 9个事务 [[‘1’, ‘2’, ‘5’],[‘2’, ‘4’],[‘2’, ‘3’],
[‘1’, ‘2’, ‘4’],[‘1’, ‘3’],[‘2’, ‘3’],
[‘1’, ‘3’],[‘1’, ‘2’, ‘3’, ‘5’],[‘1’, ‘2’, ‘3’]] - 请使用Apriori算法发现D中的频繁项集。
分析
- 设置最小支持度、导入数据
- 将项目集提取出来
- C1 计算支持度(小于支持度的剔除) L1
- C2=L1间组合,剪枝,计算支持度(小于支持度的剔除) L2 判定L2是否为空
- 直到候选频繁X-项集LX为空
- 得到频繁项集后,使用set的issubset()得到最大频繁项集
代码
#得到C1
def filterC1():
sum = []
#筛选出数据中的所有元素
[sum.append(j) for i in rawData for j in i if j not in sum]
return list(map(set, sum))
#计算支持度: cx->lx
def support(cx):
#创建lx空表
lx = []
for i in cx:
#计算各集合在数据中出现的次数
ip = 0
for j in rawData:
if i.issubset(j):
ip += 1
#若该支持度大于最小支持度,则将该集合写入lx列表
if ip/len(rawData) >= minSupport:
lx.append(i)
#若小于,则将该集合写入剪枝列表中
else:
pruning.append(i)
return lx
#无重复组合+剪枝 lx->c(x+1)
def noRepCom(lx):
#创建cx空表
cx = []
#对当前集合扩增一位,外加剪枝操作
for i in lx:
for j in l1:
#判定l1中的元素是否在该集合中,若不在
if j.issubset(i) == False:
#使用集合类型的 “与” 运算,得到新的组合
b = i | j
#如果剪枝列表存在需要剪枝元素
if len(pruning) != 0:
for k in pruning:
#剪枝集合如果不在新的组合中(剪枝),同时b不在cx的列表中(去重)
if k.issubset(b) == False and b not in cx:
cx.append(b)
#如果剪枝列表为空
else:
#只需去重即可
if b not in cx:
cx.append(b)
return cx
#main
#导入数据
rawData = [['1', '2', '5'],['2', '4'],['2', '3'],['1', '2', '4'],
['1', '3'],['2', '3'],['1', '3'],['1', '2', '3', '5'],
['1', '2', '3']]
#将数据转换为集合的数据类型
rawData = list(map(set, rawData))
print(rawData)
#设置最小支持度
minSupport = 0.3
#剪枝组
pruning = []
#得到c1
cn = filterC1()
print('c1:', cn)
#计算支持度,得到 l1
l1 = support(cn)
print('l1:', l1)
#打印剪枝列表
print('pruning', pruning, '\n')
#lx对l1进行切片,得到新的列表
#原因:在noRepCom(lx)中使用了 l1,同时为了在下面的死循环找到进入循环的入口
lx = l1[:]
#n为计算次数,sum为频繁项集
n, sum = 2, []
#Apriori算法循环
while(len(cn) != 0):
#例子:由l1得到c2
cn = noRepCom(lx)
print('c'+str(n)+':', cn)
#例子,由c2得到l1
lx = support(cn)
print('l'+str(n)+':', lx)
#打印剪枝列表
print('pruning', pruning, '\n')
#步数+1
n += 1
#累积频繁项集
sum += cn
print('D的频繁项集:',sum)
#找到sum中的最大频繁项集
for i in range(len(sum)):
#设置flag标记,初始化为 1
flag = 1
for j in range(i+1, len(sum)):
#如果sum[j]集合包含了sum[i]集合,则将flag标记置为1,退出当前循环
if sum[i].issubset(sum[j]) == True:
flag = 0
break
#若flag仍为1,则打印它因为它就是我们想要的最大频繁项集
if flag == 1:
print('D中的最大频繁项集:',sum[i])
测试
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
拓展