associati analysis
关联分析用来在大规模数据中发现某些潜在的关系,这些关系可以有两种形式
- 频繁项集:经常出现在一起的东西的集合
- 关联规则:两种东西之间存在强关系
concept
频繁项集中的频繁如何来度量呢?
用支持度和可信度来度量
项集:不同物品组成的集合
**支持度(support)**表示X,Y同时出现的概率,公式表示如下
**置信度(confidence)**表示发生X的集合中,出现Y的概率,即
提升度(lift),衡量X,Y之间的相关性,如果lift>1表示两者之间存在关联性(会一起出现);lift=1表示两者之间没有关系(两个事件相互独立);lift<1则表示两者之间可能存在替代性(一方出现,另一方就不出现了)。
所有的关联规则算法大致分成两个阶段:
- 根据minSupport的阈值来挖掘频繁集
- 定义confidence(X <= Y) 的阈值来挖掘关联规则
搜寻频繁项集—Apriori
Apriori是一个基于搜索的算法,是一个候选集生成的算法,用的是广度优先搜索, 用来发现频繁项集的算法。
图解Apriori算法的过程:
我觉得这个图对于Apriori算法的描述已经十分的清晰了
apriori算法有两个先验条件:
- 如果一个项集不是频繁项集,那么这个项集的超集(父集)也不是频繁项集
- 如果一个项集是频繁项集,那么这个项集的子集也是频繁项集
apriori算法有两个输入:最小支持度minSupport,数据集
Apriori算法中的频繁项集产生方法
在Apriori算法中,用到了两条先验原理:<1>如果一个项集不是频繁项集,那么该项集的超集也必定不是频繁项集;<2>如果一个项集是频繁项集,那么该项集的子集也是频繁项集,利用这两条先验原理可以大大较少候选频繁项集的数量。
Apriori算法的思想就是:生成项集中所有子集的全排列,对这些全排列进行逐一遍历,与数据集中的每个样本进行逐一比对,记录符合支持度的子集
比如我们数据集中有 4 个商品,商品0, 商品1,商品2,商品3,那么他们可以写成一个集合{0, 1, 2, 3},那么此时就要生成{0}, {1}, {2}, {3}, {0,1}, {0,2}, {0,3}…{0,1,2,3},然后计算每一个的支持度,保留符合支持度的集合,这些集合就是频繁项集。
Apriori的算法步骤:
- 生成所有项集的全排列,遍历整个数据集,找出所有的 1-项(项集中只包含一个物品)的项集,如[{A}, {B}, {C}](因为只有知道了所有的1-项后面才能生成2-项,直到不能生成项为止),根据最小支持度minSupport筛选出的频繁项集。
- 遍历前一步得到的频繁项集列表,生成 2-项项集,再遍历整个数据集,找出2-项的频繁项集,一直到不能生成频繁项集为止
注意:第二步中,由(k-1)-项 生成 k-项的项集,要对(k-1)-项自身的集合进行遍历,在自身的集合中找出两个集合进行并集处理,来得到 k-项的项集,但这个两个要进行并集的(k-1)-项的项集必须保证前 k-2 项是相同的,比如设当前的 k=3,有一个项集{A, B, C} 和一个项集{A, D, E},他们的并集就是{A, B, C, D, E},此时这个项集是k=5了,不满足k=4,要保证前k-2项相同如{A, B, C} 和 {A, B, D},并集才能生成{A, B, C, D}, 所以在由(k-1)-项的项集生成k-项的项集时,需要对其进行排序
生成关联规则
关联规则是从频繁项集中提取的,因为在Apriori算法中已经记录了每个频繁项集及其对应的支持度 S u p p o r t Support Support,所以生成关联规则的时候,只需遍历所有的频繁项集即可,不用重新遍历数据集。
生成关联规则的步骤:
关联规则的生成算法的每一步的作用对象,是一个频繁项集!!!一个频繁项集!!!,思想也是一样的,对这个频繁项集,找出它所有的 “全排列”(也是先找到当前操作的频繁项集freqSet的所有1-项项集,根据所有的1-项项集得到freqSet与其的差集,然后生成所有的2-项,根据所有的1-项项集得到freqSet与其的差集,直到不能生成为止)如{A, B, C},就要生成 { A } → { B , C } {\{A}\}\rightarrow{\{B, C\}} {A}→{B,C}, { B } → { A , C } {\{B}\}\rightarrow{\{A, C\}} {B}→{A,C}, { A , B } → { C } {\{A,B}\}\rightarrow{\{C\}} {A,B}→{C}等,直到枚举完所有的可能,值得注意的是: { A } → { B , C } {\{A}\}\rightarrow{\{B, C\}} {A}→{B,C} 和 { B , C } → { A } {\{B,C}\}\rightarrow{\{A\}} {B,C}→{A}是两个不同的关联规则,根据置信度Confidence的定义,他们的分母分别是 C o u n t ( A ) Count(A) Count(A) 和 C o u n t ( B , C ) Count(B,C) Count(B,C),是不一样的。
在生成频繁项集时,可以依据两条先验规则减少计算量,而在提取关联规则时,只有一条规则可以利用:如果规则
X
→
Y
X\rightarrow Y
X→Y不满足置信度要求,那么
X
−
X
‘
→
Y
+
X
‘
X-X^{`}\rightarrow Y+X^{`}
X−X‘→Y+X‘也不满足置信度要求,其中
X
‘
X^{`}
X‘是
X
X
X的子集。这条规则可以这样理解,假设置信度阈值为
α
\alpha
α,则有
S
(
X
,
Y
)
S
(
X
)
<
α
\frac{S(X,Y)}{S(X)}<\alpha
S(X)S(X,Y)<α
由于
X
‘
X^{`}
X‘ 是
X
X
X的子集,因此
X
‘
X^{`}
X‘ 的支持度一定不小于X,假设
X
‘
=
X
+
k
X^{`}=X+k
X‘=X+k ,则有
S
(
X
−
X
‘
,
Y
+
X
‘
)
S
(
X
−
X
‘
)
=
S
(
X
,
Y
)
S
(
X
)
+
k
<
S
(
X
,
Y
)
S
(
X
)
<
α
\frac{S(X-X^{`},Y+X^{`})}{S(X-X^{`})}=\frac{S(X,Y)}{S(X)+k}<\frac{S(X,Y)}{S(X)}<\alpha
S(X−X‘)S(X−X‘,Y+X‘)=S(X)+kS(X,Y)<S(X)S(X,Y)<α
代码实现:
'''
1.找到频繁项集
2.发现关联规则
Apriori 参数: 最小支持度support, 数据集
'''
from numpy import *
# todo:寻找频繁项集
# 加载数据集
def loadDataSet():
return [[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]]
# 生成所有单个物品的项集列表
def creatC1(dataSet):
'''
dataSet 二维数组
'''
C1 = list()
for transaction in dataSet:
for item in transaction:
if [item] not in C1:
C1.append([item])
C1.sort()
# 使用frozenset为了后面可以将这些值作为字典的键
return list(map(frozenset, C1))
# 过滤掉不符合最小支持度的集合
# 返回 频繁项集列表 retList, 所有元素的支持度子字典
def scanD(D, Ck, minSupport):
'''
Arg:
D:DataSet
Ck:第k个候选项集 [{}, {}, {}]
因为后面肯定是从候选项集1,即候选项集中每一项都是1个元素,
到候选项集2,候选项集中每一项都是2个元素
variable:
ssCnt <dict> :计算候选项集中的每一项在数据集中出现的次数,后面用来计算 支持度support
supportData <dict> :键为一个项集,值为该项集的支持度support
retList <list> :将所有支持度大于阈值的项集即频繁项集保存到此列表 [{'a', 'b'}, {'a', 'c'}]
return:
retList
supportData
'''
ssCnt = dict()
# 把数据集和候选项集 Ck 的每一条数据进行逐一比对
for tid in D:
for can in Ck:
# 判断子集
if can.issubset(tid):
if can not in ssCnt:
ssCnt[can] = 1
else:
ssCnt[can] += 1
numItems = float(len(D))
retList = []
supportData = dict()
for key in ssCnt:
support = ssCnt[key] / numItems
if support >= minSupport:
retList.insert(0, key)
supportData[key] = support
return retList, supportData
# 将 L_{k-1} 的项集列表转换为 Lk 的项集列表
def aprioriGen(Lk, k):
'''
Arg:
Lk 频繁项集列表 [{'a', 'b'}, {'a', 'c'}, {'b', 'c'}]
k 项集元素的个数
todo:
从每个项集中元素个数为 k-1 个的候选项集,生成每个项集中元素个数为 k 个的候选项集
attention:
要生成Lk,要遍历 L_{k-1} 中的每个元素,两两进行并集操作,但是为了保证并集得到的集合的元素个数为 k 个,
就要保证 L_{k-1} 中前 k-2 个元素都是相同的,如果不相同如 L3 中的一个 {'a', 'b', 'c} 和 L3 中的另一个
{'b', 'f', 'd'} 就会生成一个 L5 的集合 {'a', 'b', 'c', 'd', 'f'},而 {'a', 'b', 'c} 和 {'e', 'b', 'c}
就能生成一个 L4 的集合 {'a', 'b', 'c', 'd'},所以要对 L_{k-1} 中的前 k-2 个元素进行排序并比对
'''
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:
# 求两者的并集
retList.append(Lk[i] | Lk[j])
return retList
def apriori(dataSet, minSupport=0.5):
'''
Arg:
dataSet: 二维数组
minSupport: 最小支持度
variable:
C: 项集列表,每一项为一个集合
D: 对数据集<二维数组>的每一行进行集合处理
L: 存储所有频繁项集的列表
algorithm:
从每一项为 1个元素的集合的候选集生成频繁项集,一直到 k个元素的集合的候选集生成频繁项集
直到不能在生成频繁项集为止
'''
C1 = creatC1(dataSet)
D = map(set, dataSet)
print('D=', D)
L1, supportData = scanD(D, C1, minSupport)
L = [L1]
k = 2
while (len(L[k-2]) > 0):
print('k=', k, L, L[k-2])
Ck = aprioriGen(L[k-2], k)
print('Ck', Ck)
Lk, supK = scanD(D, Ck, minSupport)
supportData.update(supK)
if len(Lk) == 0:
break
L.append(Lk)
k += 1
return L, supportData
# todo: 提取关联规则
def calcConf(freqSet, H, supportData, brl, minConf=0.7):
"""calcConf(对两个元素的频繁项,计算可信度,例如: {1,2}/{1} 或者 {1,2}/{2} 看是否满足条件)
Args:
freqSet 频繁项集中的元素,例如: frozenset([1, 3])
H 频繁项集中的元素的集合,例如: [frozenset([1]), frozenset([3])]
supportData 所有元素的支持度的字典
brl即 bigrulelist 用来记录所有的关联规则[(freqSet-conseq, conseq, conf)]
minConf 最小可信度
Returns:
prunedH 记录 可信度大于阈值的集合
"""
prunedH = []
for conseq in H:
conf = supportData[freqSet] / supportData[freqSet-conseq]
if conf >= minConf:
print(freqSet - conseq, '-->', conseq, 'conf:', conf)
brl.append((freqSet-conseq, conseq, conf))
prunedH.append(conseq)
return prunedH # 满足最小置信度要求的每一项为单个元素的集合
def rulesFromConseq(freqSet, H, supportData, brl, minConf=0.7):
"""rulesFromConseq
Args:
freqSet 频繁项集中的元素,例如: frozenset([2, 3, 5])
H 频繁项集中的元素的集合,例如: [frozenset([2]), frozenset([3]), frozenset([5])]
supportData 所有元素的支持度的字典
brl即 bigrulelist 用来记录所有的关联规则[(freqSet-conseq, conseq, conf)]
minConf 最小可信度
"""
# H[0] 是 freqSet 的元素组合的第一个元素,并且 H 中所有元素的长度都一样,长度由 aprioriGen(H, m+1) 这里的 m + 1 来控制
# 该函数递归时,H[0] 的长度从 1 开始增长 1 2 3 ...
# 假设 freqSet = frozenset([2, 3, 5]), H = [frozenset([2]), frozenset([3]), frozenset([5])]
# 那么 m = len(H[0]) 的递归的值依次为 1 2
# 在 m = 2 时, 跳出该递归。假设再递归一次,那么 H[0] = frozenset([2, 3, 5]),freqSet = frozenset([2, 3, 5]) ,没必要再计算 freqSet 与 H[0] 的关联规则了。
m = len(H[0]) # 获得 Lk 的长度
# 如果当前长度大于 Lk 的长度就不用在递归下去了
if (len(freqSet) > (m + 1)):
print('freqSet*******', len(freqSet), m+1, freqSet, H, H[0])
Hmp1 = aprioriGen(H, m+1)
Hmp1 = calcConf(freqSet, Hmp1, supportData, brl, minConf)
# 到最后穷举完所有的组合 len(Hmp1)就等于 1,剩下它自己了
if(len(Hmp1) > 1):
rulesFromConseq(freqSet, Hmp1, supportData, brl, minConf)
def generateRules(L, supportData, minConf=0.7):
'''
Arg:
L: 频繁项集列表
'''
bigRuleList = []
for i in range(1, len(L)):
for freqSet in L[i]:
H1 = [frozenset([item]) for item in freqSet]
if (i > 1):
rulesFromConseq(freqSet, H1, supportData, bigRuleList, minConf)
else:
calcConf(freqSet, H1, supportData, bigRuleList, minConf)
return bigRuleList
使用mlxtend库实现
https://blog.csdn.net/qq_36523839/article/details/83960195
参考博客:
https://blog.csdn.net/huguozhiengr/article/details/82869591
https://ailearning.apachecn.org/#/docs/ml/11.%E4%BD%BF%E7%94%A8Apriori%E7%AE%97%E6%B3%95%E8%BF%9B%E8%A1%8C%E5%85%B3%E8%81%94%E5%88%86%E6%9E%90