1 关联规则挖掘
1.1 理论定义
-
支持度(support):数据集中包含该项集的数据所占数据集的比例,度量一个集合在原始数据中出现的频率;
-
置信度(confidence):针对关联规则定义如 c o n f i d e n t ( A → B ) = s u p p o r t ( A ∪ B ) s u p p o r t ( A ) confident(A\rightarrow B) = \frac{support(A \cup B)}{support(A)} confident(A→B)=support(A)support(A∪B),可以理解为在事件A发生的条件下,B发生的概率。
-
频繁项集:经常出现在一块的项的集合。
-
关联规则:形如 A → B A \rightarrow B A→B,当 c o n f i d e n t ( A → B ) confident(A \rightarrow B) confident(A→B)较高时,表明事件A发生的同时,往往伴随着事件B的发生。
例,现有超市购物篮数据集如下
表中的每一行代表一次购买清单,清单中的每一个商品称为项,清单中任意项的组合称之为项集,清单中所有项的集合称为总项集。上表中的总项集为:S = {牛奶,面包,尿布,啤酒,鸡蛋,可乐}。
根据支持度和置信度的定义有:
s
u
p
p
o
r
t
(
啤
酒
)
=
3
5
=
0.6
support(啤酒)=\frac{3}{5}=0.6
support(啤酒)=53=0.6
s u p p o r t ( 啤 酒 , 尿 布 ) = 3 5 = 0.6 support(啤酒,尿布) = \frac{3}{5} = 0.6 support(啤酒,尿布)=53=0.6
c o n f i d e n t ( 啤 酒 → 尿 布 ) = s u p p o r t ( 啤 酒 , 尿 布 ) s u p p o r t ( 啤 酒 ) = 0.6 0.6 = 1 confident(啤酒 \rightarrow 尿布) = \frac{support(啤酒,尿布)}{support(啤酒)}=\frac{0.6}{0.6}=1 confident(啤酒→尿布)=support(啤酒)support(啤酒,尿布)=0.60.6=1
1.2 现实意义
支持度越高表明项出现越频繁,即对于商品来讲越受欢迎,销量高。
置信度越高表明在某项出现的时候,往往伴随则另一些项的出现,如上述示例中尿布的购买往往伴随着啤酒的购买。
对于超市经营者,如果能够知道支持度高的商品即销量高的商品,那么可以增加对这类商品的投入。更进一步,如果经营者知道有哪些商品与销量高的商品经常一起购买,那么经营者就可以考虑缩短这些商品在超市中的物理距离,可以摆放临近的货架,对于互补品也可以考虑捆绑销售的促销策略,以期获得更高的收益。
1.3 算法实现
对于一个包含 n n n个项的总项集来讲,它的子项集有 2 n − 1 2^n-1 2n−1个(不考虑空项集),显然并不是每一个子项集都有意义,同样并不是每一个挖掘到的规则都有意义。那么如何判断挖掘到的子项集或者规则是否意义?
实际中,较为普遍地是通过设定支持度阈值来区分频繁项集和非频繁项集;置信度阈值区分强关联规则与弱关联规则。
关联规则的目标分为发现频繁项集(满足最小支持度要求的项集)和发现关联规则(满足最小置信度要求的规则)两部分。关联规则的发现建立在频繁项集的基础上,因此下面我们首先介绍Apriori算法。
1.3.1 Apriori算法
Apriori算法伪代码
以表1超市购物篮数据为例进行频繁项集的挖掘,设定支持度阈值 α = 0.6 \alpha=0.6 α=0.6。具体挖掘流程如下图所示:
在支持度阈值
α
=
0.6
\alpha=0.6
α=0.6时,无频繁3项集,该数据集的最大频繁K项集为频繁2项集即:F2。
注意:由频繁2项集F2生成候选频繁3项集时无{牛奶,面包,啤酒},{牛奶,尿布,啤酒},{面包,尿布,啤酒}这三种组合,是因为{牛奶,啤酒},{面包,啤酒}不是频繁项集,详见Apriori定律。
1.3.2 Apriori定律
如果某个项集是频繁的,那么它的所有子集也是频繁的(例:如果{B,C}为频繁项集,那么{B},{C}也一定为频繁项集);
如果某个项集是非频繁的,那么它的所有超集(包含非频繁项集的超集)也是非频繁的(例:如果{A,B}是非频繁的,那么{A,B,C},{A,B,C,D}也一定是非频繁的)。
1.3.3 关联规则的挖掘
关联规则建立在频繁项集的基础之上。
对给定频繁项集 I I I进行关联规则挖掘算法流程图如下:
注:如果频繁项集内的某条规则并不满足最小置信度要求,那么该规则的所有子集也不会满足最小置信度的要求
简单证明:对于频繁项集
I
I
I,关联规则
(
S
−
>
I
−
S
)
(S->I-S)
(S−>I−S)的置信度为
c
o
n
f
i
d
e
n
t
(
S
→
I
−
S
)
=
s
u
p
p
o
r
t
(
I
)
s
u
p
p
o
r
t
(
S
)
confident(S\rightarrow I-S) = \frac{support(I)}{support(S)}
confident(S→I−S)=support(S)support(I)
在给定频繁项集
I
I
I和最小置信度阈值
β
\beta
β时,关联规则
(
S
−
>
I
−
S
)
(S->I-S)
(S−>I−S)能否满足最小置信度要求,仅取决于
S
S
S的支持度大小。
s
u
p
p
o
r
t
(
S
)
support(S)
support(S)越小,
c
o
n
f
i
d
e
n
t
(
S
−
>
I
−
S
)
confident(S->I-S)
confident(S−>I−S)就越大。
由Apriori定律知: s u p p o r t ( S ) ≤ s u p p o r t ( S 的 子 集 ) support(S) \le support(S的子集) support(S)≤support(S的子集),所以当 ( S − > I − S ) (S->I-S) (S−>I−S)不满足最小置信度阈值要求时,在当前频繁项集 I I I中,所有 S S S的子集 R R R,形成的 ( R − > I − R ) (R->I-R) (R−>I−R)均不满足最小置信度阈值要求。
1.4 代码实现
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@File : Apriori.py
@Time : 2022/03/15 19:51:22
@Author : yy
@Desc : Apriori关联规则
'''
class Apriori:
def __init__(self,dataSets,k,min_support,min_Conf) -> None:
self.dataSets = [set(record) for record in dataSets]
self.k = k #频繁项集中的元素个数
self.min_num = round(min_support*len(dataSets)) #最小支持度*样本数据量
self.min_Conf = min_Conf #最小置信度
def createC1(self):
"""输出候选1项集
Returns:
C1 候选1项集[集合,计数]
"""
new_data = []
for record in self.dataSets:
new_data.extend(list(record))
items1 = set(new_data)
C1 = [] #候选项集
for item in items1:
C1.append([{item},new_data.count(item)])
return C1
def frequent(self,C):
"""从候选k项集C中挖掘频繁k项集
Args:
C 候选k项集,[集合,计数]
Returns:
L 频繁k项集[集合,计数]
"""
L = []
for row in C:
if row[-1] >= self.min_num:
L.append(row)
return L
def createC(self,L,L1):
"""计算k频繁项集候选集
Args:
L k-1频繁项集[集合,计数]
L1 1频繁项集[集合,计数]
dataSetsT 样本记录转换为set后的数据集[set,set,...]
Returns:
Ck k频繁项集候选集[集合,计数],计数可能为0
"""
#记录所有可能的候选项集
C0 = []
for items in L:
for item in L1:
if not item[0].issubset(items[0]):
newitems0 = items[0].copy()
newitems0.update(item[0])
if newitems0 not in C0:
C0.append(newitems0)
Ck = [ [newitems,0] for newitems in C0]
for row in Ck:
#计算频繁项集候选集在样本数据中出现的次数,显然会有可能出现后选项集计数为0的情况
for record in self.dataSets:
if row[0].issubset(record):
row[-1] += 1
return Ck
def aprioriK(self):
"""在给定样本数据集和最小支持度的情况下,输出k频繁项集
Returns:
L k频繁项集
"""
#初始化1频繁项集
C1 = self.createC1()
L1 = self.frequent(C1)
L = L1.copy()
K = self.k
while K-1:
C = self.createC(L,L1)
L = self.frequent(C)
K -= 1
return L
def calcConf(self,I,S):
"""计算S->I-S的置信度
Args:
I 频繁项集[集合,计数]
S I中集合的非空真子集
Returns:
conf S->I-S的置信度
"""
Scount = 0
for items in self.dataSets:
if S.issubset(items):
Scount += 1
conf = I[-1] / Scount
return conf
def generateRule(self,frequentSet):
"""生成频繁项集的非空子集,并根据最小置信度筛选出当前频繁项集中的规则
Args:
frequentSet 频繁项集[集合,计数]
"""
associationRules = [] #存储子集
I = list(frequentSet[0])
#集合子集的生成思想,集合元素存在在于不在子集中两种状态,可以用0-1二进制表示。
for i in range(1 << len(I)): #按位左移,如1<<2-->则为4,全为0的情况不考虑
S = set()
for j in range(len(I)):
if i & (1 << j): #每个数用&操作判断该位上是否有1,参与运算的两个值,如果两个相应位都为1,则该位的结果为1
S.add(I[j])
if len(S) == 0 or len(S) == len(I):
continue
conf = self.calcConf(frequentSet,S)
if conf >= self.min_Conf:
associationRules.append(['{}-->{}'.format(S,frequentSet[0]-S),"%.2f"%conf])
return associationRules
def aprioriMining(self):
L = self.aprioriK() #频繁项集
rules = [] #挖掘出的关联规则
for frequentSet in L:
rules.extend(self.generateRule(frequentSet))
return rules
def loadDataSet(flag=1):
if flag == 1:
dataSets = [['A', 'C', 'D'], ['B', 'C', 'E'], ['A', 'B', 'C', 'E'], ['B', 'E']]
else:
dataSets = [['牛奶','面包'],['面包','尿布','啤酒','鸡蛋'],['牛奶','尿布','啤酒','可乐'],['面包','牛奶','尿布','啤酒'],['面包','牛奶','尿布','可乐']]
return dataSets
def main():
dataSets = loadDataSet()
k = 3
min_support = 0.5
min_Conf = 0.5
model = Apriori(dataSets,k,min_support,min_Conf)
print(model.aprioriMining())
if __name__ == '__main__':
main()
1.5 参考链接
https://www.cnblogs.com/fengfenggirl/p/associate_apriori.html