Apriori这个词的意思是“先验的”,从priori这个词根可以猜出来~;) 。该算法用于从数据中挖掘频繁项数据集以及关联规则。其核心原理是基于这样一类“先验知识”:
如果一个数据项在数据库中是频繁出现的,那么该数据项的子集在数据库中也应该是频繁出现的(命题1)
∀X,Y∈J:(X⊆Y)→f(X)≤f(Y)∀X,Y∈J:(X⊆Y)→f(X)≤f(Y)
反之亦然,其逆否命题为:
如果一个数据项在数据库中很少出现,那么包含该数据项的父集(superset)在数据库中也应该很少出现。(命题2)
f(X)≥f(Y)→∀X,Y∈J:(X⊇Y)f(X)≥f(Y)→∀X,Y∈J:(X⊇Y)
背景知识:
①假设我们要从数据库中找到如下一种关联规则:
x→yx→y
也就是说,当某一数据项包含包含集合X时,该数据项肯定包含集合Y。
②既然说有X的地方必定有Y,那么我们需要大量的数据来说明这一点。用X和Y同时出现的次数除以数据库中数据项的总数得到“支持度”的概念:
Support,s(X→Y)=δ(X∪Y)N;Support,s(X→Y)=δ(X∪Y)N;
③在集合X出现的数据项中,是否一定会出现集合Y呢?我们用X和Y同时出现的次数除以X出现的全部次数,得到“置信度”的概念:
Confidence,c(X→Y)=δ(X∪Y)δ(X);Confidence,c(X→Y)=δ(X∪Y)δ(X);
深入理解apriori算法:
分析“支持度”和“置信度”的概念可知,在给定“支持度”和“置信度”的条件下为了找到关联规则,首先需要找到符合“支持度”条件的X和Y的并集{X,Y}。由命题1可知,如果集合{X,Y}满足“支持度”条件(即频繁出现),那么集合中的每个元素也应该是频繁出现的。集合的构成可以用树来表示,下面用图1来说明。
图 1 若{c,d,e}频繁出现,则{cd}{ce}{de},{c}{d}{e}也频繁出现
图 2如果{a,b}不是频繁集,那么{abc}{abd}{abe}{abcd}{abce}{abde}{abcde}也都不是频繁集。
由此可见,如果我们从单一元素所构成的集合下手(也就是上图中树的第一层,记为C1),根据“支持度”判别条件对该树进行“剪枝”,将大大降低计算的次数。
得到C1后,如果根据组合原理直接生成C2然后对每个可能的组合计算“支持度”,计算量依然很大。这里再次进行剪枝。为了不失一般性,对于Ck-1层中的每个集合先排序,然后将满足以下条件的集合融合,构成Ck层
ai=bi(fori=1,2,...,k−2)andak−1≠bk−1ai=bi(fori=1,2,...,k−2)andak−1≠bk−1
之所以这样做是因为,根据命题2,如果集合C4层的{acde}是频繁集,那么C3层中必定要存在{acd}和{ace}。因此只需在C3成对这两个集合融合即可,不必再将{ace}和{ade}融合,在C3层对元素排序的目的也正是在此,快速地找到满足条件的子集并融合,避免重复计算。
优化:
在得到Ck层后,计算其中每个集合的“支持度”时,需要从数据库中遍历所有的数据项看是否包含该集合。这里可以采用Hash表将所有的数据映射到一张表上,以后就不用遍历整个数据库而是只遍历Hash值相同的所有数据项。
生成规则:
对于前面得到的频繁项集合中每个元素,其可能生成的规则可以表示为下图
图3 从频繁项生成规则
以上图为例来说明,假设由{bcd}生成{a}这一规则不满足置信度公式,回顾“置信度”的公式,也就是说{bcd}在数据库中出现的次数偏多,而{a}出现的次数偏少,根据命题1,{bcd}的子集也是频繁项,根据命题2,{a}的父集也很少出现,从而{bc}生成{ad}等规则的置信度更低,然后将其从集合树上减去。
总结:
将以上过程联系起来,就得到了书上的伪代码,我将其用通俗的语言解释一下:
1. 遍历数据库,得到所有数据项构成的并集(也就是得到图1的C1层)
2. 计算Ck层中每个元素的支持度(该过程可用Hash表优化),删除不符合的元素,将剩下的元素排序,并加入频繁项集R
3. 根据融合规则将Ck层的元素融合得到Ck+1,
4. 重复2,3步直到某一层元素融合后得到的是空集
5. 遍历R中的元素,设该元素为A={a1,a2......,ak}
6. 按照图 所示方法先生成I1层规则,即{x|x属于A且≠ai} →{ai}
7. 计算该层所有规则的“置信度”,删除不符合的规则,将剩下的规则作为结果输出。
8. 生成下一层的规则,计算“置信度”,输出结果。
参考文献:
Machine Learning in Action:http://pan.baidu.com/s/1Gc4ss
Introduction to Data Mining chapter 6 :http://pan.baidu.com/s/1oskIS
Python源码:
去GitHub下载该文件源码
01
from
numpy
import
*
02
import
itertools
03
04
support_dic
=
{}
05
06
#生成原始数据,用于测试
07
def
loadDataSet
():
08
return
[[
1
,
3
,
4
],
[
2
,
3
,
5
],
[
1
,
2
,
3
,
5
],
[
2
,
5
]]
09
10
#获取整个数据库中的一阶元素
11
def
createC1(
dataSet
):
12
C1
=
set
([])
13
for
item
in
dataSet
:
14
C1
=
C1
.
union(
set(
item))
15
return
[
frozenset
([
i
])
for
i
in
C1
]
16
17
#输入数据库(dataset) 和 由第K-1层数据融合后得到的第K层数据集(Ck),
18
#用最小支持度(minSupport)对 Ck 过滤,得到第k层剩下的数据集合(Lk)
19
def
getLk(
dataset
,
Ck
,
minSupport
):
20
global
support_dic
21
Lk
=
{}
22
#计算Ck中每个元素在数据库中出现次数
23
for
item
in
dataset
:
24
for
Ci
in
Ck
:
25
if
Ci
.
issubset(
item
):
26
if
not
Ci
in
Lk
:
27
Lk
[
Ci
]
=
1
28
else
:
29
Lk
[
Ci
]
+=
1
30
#用最小支持度过滤
31
Lk_return
=
[]
32
for
Li
in
Lk
:
33
support_Li
=
Lk
[
Li
]
/
float(
len(
dataSet))
34
if
support_Li
>=
minSupport
:
35
Lk_return
.
append(
Li)
36
support_dic
[
Li
]
=
support_Li
37
return
Lk_return
38
39
#将经过支持度过滤后的第K层数据集合(Lk)融合
40
#得到第k+1层原始数据Ck1
41
def
genLk1(
Lk
):
42
Ck1
=
[]
43
for
i
in
range(
len(
Lk)
-
1
):
44
for
j
in
range(
i
+
1
,
len(
Lk
)):
45
if
sorted(
list(
Lk
[
i
]))[
0
:
-
1
]
==
sorted(
list(
Lk
[
j
]))[
0
:
-
1
]:
46
Ck1
.
append(
Lk
[
i
]
|
Lk
[
j
])
47
return
Ck1
48
49
#遍历所有二阶及以上的频繁项集合
50
def
genItem(
freqSet
,
support_dic
):
51
for
i
in
range(
1
,
len(
freqSet
)):
52
for
freItem
in
freqSet
[
i
]:
53
genRule(
freItem)
54
55
#输入一个频繁项,根据“置信度”生成规则
56
#采用了递归,对规则树进行剪枝
57
def
genRule(
Item
,
minConf
=
0.7
):
58
if
len(
Item)
>=
2
:
59
for
element
in
itertools
.
combinations(
list(
Item
),
1
):
60
if
support_dic
[
Item
]
/
float(
support_dic
[
Item
-
frozenset(
element
)])
>=
minConf
:
61
print
str
([
Item
-
frozenset(
element
)])
+
"—–>"
+
str(
element)
62
print
support_dic
[
Item
]
/
float(
support_dic
[
Item
-
frozenset(
element
)])
63
genRule(
Item
-
frozenset(
element))
64
65
#输出结果
66
if
name
==
'main'
:
67
dataSet
=
loadDataSet()
68
result_list
=
[]
69
Ck
=
createC1(
dataSet)
70
#循环生成频繁项集合,直至产生空集
71
while
True
:
72
Lk
=
getLk(
dataSet
,
Ck
,
0.5)
73
if
not
Lk
:
74
break
75
result_list
.
append(
Lk)
76
Ck
=
genLk1(
Lk)
77
if
not
Ck
:
78
break
79
#输出频繁项及其“支持度”
80
print
support_dic
81
#输出规则
82
genItem(
result_list
,
support_dic)
from: http://tianjun.me/essays/17/