Apriori算法
Agrawal与Srikant于1994年提出,为布尔关联规则挖掘频繁项集的原创性算法,使用一种称为逐层搜索的迭代方法,其中k项集用于探索k+1项集。
主要思想是找出存在于事务数据集中的最大的频繁项集,再利用得到的最大频繁项集和预先设定的最小置信度阈值生成强关联规则
1. 重要概念
(1)关联规则支持度和置信度
项集A、B同时发生的概率称为关联规则的支持度
S
u
p
p
o
r
t
(
A
−
>
B
)
=
P
(
A
U
B
)
Support(A->B) = P(AU B)
Support(A−>B)=P(AUB)
项集A发生,则项集B发生的概率称为关联规则的置信度
C
o
n
f
i
d
e
n
c
e
(
A
−
>
B
)
=
P
(
B
∣
A
)
Confidence(A->B) = P(B|A)
Confidence(A−>B)=P(B∣A)
(2)最小支持度和最小置信度
最小支持度是用户或专家定义的衡量支持度的一个阈值,表示项集在统计意义上的最低重要性;最小置信度用用户或专家定义的衡量置信度的一个阈值,表示关联规则的最低可靠性,同时满足最小支持度阈值和最小置信度阈值的规则称为强规则。
(3)项集
项集是项的集合,包含k个项的集合称为k项集。项集的出现频率是所有包含项集的事务计数。如果项集I的相对支持度满足预定义的最小支持度阈值,则I是频繁项集
(4)支持度计数
项集A的支持度计数是事务数据集中包含项集A的事务个数,简称为项集的频率或计数。
S
u
p
p
o
r
t
(
A
−
>
B
)
=
A
,
B
同
时
发
生
的
事
务
个
数
/
所
有
事
务
个
数
Support(A->B) = A,B同时发生的事务个数/所有事务个数
Support(A−>B)=A,B同时发生的事务个数/所有事务个数
C
o
n
f
i
d
e
n
c
e
(
A
−
>
B
)
=
A
,
B
同
时
发
生
的
事
务
个
数
/
A
发
生
的
事
务
个
数
Confidence(A->B) = A,B同时发生的事务个数/A发生的事务个数
Confidence(A−>B)=A,B同时发生的事务个数/A发生的事务个数
2. 两个重要定理
1.如果一个集合是频繁项集,则它的所有子集都是频繁项集。假设一个集合{A,B}是频繁项集,则它的子集{A}, {B} 都是频繁项集。
2.如果一个集合不是频繁项集,则它的所有超集都不是频繁项集。假设集合{A}不是频繁项集,则它的任何超集如{A,B},{A,B,C}必定也不是频繁项集。
3. Apriori算法实现过程
(1).首先找到最大的频繁项集
L
k
L_k
Lk,由连接步和剪枝步组成
a.连接步:主要目的是找到K项集,对给定的最小支持度阈值,分别对1项候选集
C
1
C_1
C1,剔除小于该阈值的项集得到1项频繁项集
L
1
L_1
L1,下一步有
L
1
L1
L1自身连接产生2项候选集
C
2
C_2
C2,保留
C
2
C_2
C2中满足约束条件的项集得到2项频繁项集
L
2
L_2
L2,下一步再由
L
2
L_2
L2和
L
1
L_1
L1连接产生3项候选集
C
3
C_3
C3,保留
C
3
C_3
C3中满足约束条件的项集得到3项频繁项集
L
3
L_3
L3…,如此循环下去,得到最大频繁项集
L
k
L_k
Lk。
b.剪枝步:主要目的是在产生候选项集
C
k
C_k
Ck的过程中起到减小搜索空间的目的,由于
C
k
C_k
Ck是由
L
k
−
1
L_k-1
Lk−1和
L
1
L_1
L1连接产生的,根据定理一个集合是频繁项集,则它的所有子集都是频繁项集可知,所以不满足该性质的项集也不会存在于
C
k
C_k
Ck中。
(2).由频繁项集产生强关联规则
一旦找出频繁项集,就可以直接由它们产生强关联规则(强关联规则满足最小支持度和最小置信度),关联规则可以产生如下:
1).对于每个频繁项集
l
l
l,产生
l
l
l的所有非空子集
2).对于
l
l
l的每个非空子集s,如果support_count(t)/support(s) >= min_conf,则输出规则s->(l-s)。
4. 具体例子
下表D有9个事务,即|D| = 9,频繁项集产生如下:
(1)在算法的第一次迭代中,每个项都是候选1项集的集合
C
1
C_1
C1的成员,算法简单的扫描所有的事务,对每个项的出现次数计数。
(2)假设最小支持度计数为2,即min_sup=2(这里讨论的是绝对支持度,对应的相对支持度为2/9=22%),可以确定频繁1项集合
L
1
L_1
L1,在我们的例子中,
C
1
C_1
C1的所有候选都满足最小支持度。
(3)为了发现频繁2项集合
L
2
L_2
L2,算法通过连接
L
1
L_1
L1和
L
1
L_1
L1产生2项集的候选集
C
2
C_2
C2,注意,在剪枝步没有候选从
C
2
C2
C2中删除,因为这些候选集的每个子集也是频繁的。
(4)扫描D中事务,累计
C
2
C_2
C2中每个候选集的支持计数。
(5)然后,确定频繁2项集
L
2
L_2
L2,它由
C
2
C_2
C2中满足最小支持度的候选2项集组成。
(6)在确定
L
3
L_3
L3时就不必要求它的计数值,只需要检查它的
k
−
1
k-1
k−1项子集是否频繁。
(7)产生4项集合
C
4
C_4
C4,由于其非空子集含非频繁项集被剪枝,所以最大的频繁项集为
L
3
L_3
L3。
5. python代码
# -*- coding: utf-8 -*-
"""
两个重要定理:
1.如果一个集合是频繁项集,则它的所有子集都是频繁项集。假设一个集合{A,B}是频繁项集,则它的子集{A}, {B} 都是频繁项集。
2.如果一个集合不是频繁项集,则它的所有超集都不是频繁项集。假设集合{A}不是频繁项集,则它的任何超集如{A,B},{A,B,C}必定也不是频繁项集。
"""
def load_data_set():
data_set = [['e1', 'e2', 'e5'], ['e2', 'e4'], ['e2', 'e3'], ['e1', 'e2', 'e4'], ['e1', 'e3'], ['e2', 'e3'],
['e1', 'e3'], ['e1', 'e2', 'e3', 'e5'], ['e1', 'e2', 'e3']]
return data_set
def Create_C1(data_set):
C1 = set()
for t in data_set:
for item in t:
item_set = frozenset([item])
# 为生成频繁项目集时扫描数据库时以提供issubset()功能
C1.add(item_set)
return C1
# 判断候选集合是不是频繁项集,如果一个集合不是频繁项集,那么它所有的超集都不可能是频繁项集
def is_apriori(Ck_item, Lk_sub_1):
for item in Ck_item:
subitem = Ck_item - frozenset([item])
if subitem not in Lk_sub_1:
return False
return True
# 创建频繁候选项集
def Create_Ck(Lk_sub_1, k):
Ck = set() # 创建一个空集合
len_lk_sub_1 = len(Lk_sub_1)
list_lk_sub_1 = list(Lk_sub_1)
for i in range(len_lk_sub_1):
for j in range(i, len_lk_sub_1):
l1 = list(list_lk_sub_1[i])
l2 = list(list_lk_sub_1[j])
l1.sort()
l2.sort()
# 判断l1的前k-1-1个元素与l2的前k-1-1个元素对应位是否全部相同
if l1[0:k - 2] == l2[0:k - 2]:
Ck_item = list_lk_sub_1[i] | list_lk_sub_1[j]
if is_apriori(Ck_item, Lk_sub_1):
Ck.add(Ck_item)
return Ck
# issubset() 方法用于判断集合的所有元素是否都包含在指定集合中,如果是则返回 True,否则返回 False
# 根据候选项集创建频繁项集
def Generate_Lk_By_Ck(data_set, Ck, min_support, support_data):
Lk = set() # 创建频繁项集集合
item_count = {}
for t in data_set:
for ck_item in Ck:
if ck_item.issubset(t):
if ck_item not in item_count:
item_count[ck_item] = 1
else:
item_count[ck_item] += 1
data_num = float(len(data_set))
for item in item_count:
if (item_count[item] / data_num) >= min_support: # 如果支持度小于最小支持度,则保留
Lk.add(item)
support_data[item] = item_count[item] / data_num
return Lk
def Generate_L(data_set, max_k, min_support):
support_data = {}
C1 = Create_C1(data_set)
L1 = Generate_Lk_By_Ck(data_set, C1, min_support, support_data)
Lk_sub_1 = L1.copy() # 对L1进行浅copy
L = list()
L.append(Lk_sub_1) # 末尾添加指定元素
for k in range(2, max_k + 1):
Ck = Create_Ck(Lk_sub_1, k)
Lk = Generate_Lk_By_Ck(data_set, Ck, min_support, support_data)
Lk_sub_1 = Lk.copy()
L.append(Lk_sub_1)
return L, support_data
def Generate_Rule(L, support_data, min_confidence):
rule_list = []
sub_set_list = []
for i in range(len(L)):
for frequent_set in L[i]:
for sub_set in sub_set_list:
if sub_set.issubset(frequent_set):
conf = support_data[frequent_set] / support_data[sub_set]
# 将rule声明为tuple
rule = (sub_set, frequent_set - sub_set, conf)
if conf >= min_confidence and rule not in rule_list:
rule_list.append(rule)
sub_set_list.append(frequent_set)
return rule_list
if __name__ == "__main__":
data_set = load_data_set()
L, support_data = Generate_L(data_set, 3, 0.22)
rule_list = Generate_Rule(L, support_data, 0.5)
for Lk in L:
print("=" * 55)
print("frequent " + str(len(list(Lk)[0])) + "-itemsets\t\tsupport")
print("=" * 55)
for frequent_set in Lk:
print(frequent_set, support_data[frequent_set])
print()
print("Rules")
for item in rule_list:
print(item[0], "=>", item[1], "'s conf: ", item[2])
运行结果:
/Users/xudong/opt/anaconda3/python.app/Contents/MacOS/python /Users/xudong/PycharmProjects/推荐算法/推荐算法/Apriori.py
=======================================================
frequent 1-itemsets support
=======================================================
frozenset({'e4'}) 0.2222222222222222
frozenset({'e3'}) 0.6666666666666666
frozenset({'e2'}) 0.7777777777777778
frozenset({'e5'}) 0.2222222222222222
frozenset({'e1'}) 0.6666666666666666
=======================================================
frequent 2-itemsets support
=======================================================
frozenset({'e2', 'e1'}) 0.4444444444444444
frozenset({'e5', 'e1'}) 0.2222222222222222
frozenset({'e1', 'e3'}) 0.4444444444444444
frozenset({'e2', 'e5'}) 0.2222222222222222
frozenset({'e2', 'e4'}) 0.2222222222222222
frozenset({'e2', 'e3'}) 0.4444444444444444
=======================================================
frequent 3-itemsets support
=======================================================
frozenset({'e2', 'e1', 'e5'}) 0.2222222222222222
frozenset({'e2', 'e1', 'e3'}) 0.2222222222222222
Rules
frozenset({'e2'}) => frozenset({'e1'}) 's conf: 0.5714285714285714
frozenset({'e1'}) => frozenset({'e2'}) 's conf: 0.6666666666666666
frozenset({'e5'}) => frozenset({'e1'}) 's conf: 1.0
frozenset({'e3'}) => frozenset({'e1'}) 's conf: 0.6666666666666666
frozenset({'e1'}) => frozenset({'e3'}) 's conf: 0.6666666666666666
frozenset({'e5'}) => frozenset({'e2'}) 's conf: 1.0
frozenset({'e4'}) => frozenset({'e2'}) 's conf: 1.0
frozenset({'e3'}) => frozenset({'e2'}) 's conf: 0.6666666666666666
frozenset({'e2'}) => frozenset({'e3'}) 's conf: 0.5714285714285714
frozenset({'e5'}) => frozenset({'e2', 'e1'}) 's conf: 1.0
frozenset({'e2', 'e1'}) => frozenset({'e5'}) 's conf: 0.5
frozenset({'e5', 'e1'}) => frozenset({'e2'}) 's conf: 1.0
frozenset({'e2', 'e5'}) => frozenset({'e1'}) 's conf: 1.0
frozenset({'e2', 'e1'}) => frozenset({'e3'}) 's conf: 0.5
frozenset({'e1', 'e3'}) => frozenset({'e2'}) 's conf: 0.5
frozenset({'e2', 'e3'}) => frozenset({'e1'}) 's conf: 0.5
Process finished with exit code 0