菊安酱的机器学习
关联分析(association analysis)是一种在大规模数据集中寻找有趣关系的非监督学习算法。
这种关系可以有两种形式: 频繁项集或者关联规则。
关联分析的一个典型例子是购物篮分析。
交易号码 | 商品 |
---|---|
001 | 豆奶,莴苣 |
002 | 莴苣,尿布,啤酒,甜菜 |
003 | 豆奶,尿布,啤酒,橙汁 |
004 | 莴苣,豆奶,尿布,啤酒 |
005 | 莴苣,豆奶,尿布,橙汁 |
在这里需要明确几个定义:
频繁项集是经常出现在一块的物品的集合,那么问题就来了:
因此需要一个评估频繁项集的标准。常用的频繁项集的评估标准有支持度、置信度和提升度三个。
支持度(support)就是几个关联的数据在数据集中出现的次数占总数据集的比重。或者说几个数据关联出现的概率。
分析关联性的数据X和Y(2-项集),则对应的支持度为:
S u p p o r t ( X , Y ) = P ( X Y ) = n u m b e r ( X Y ) n u m ( A l l S a m p l e s ) Support\left( X,Y \right) =P\left( XY \right) =\frac{number\left( XY \right)}{num\left( AllSamples \right)} Support(X,Y)=P(XY)=num(AllSamples)number(XY)
例
交易号码 | 商品 |
---|---|
001 | 豆奶,莴苣 |
002 | 莴苣,尿布,啤酒,甜菜 |
003 | 豆奶,尿布,啤酒,橙汁 |
004 | 莴苣,豆奶,尿布,啤酒 |
005 | 莴苣,豆奶,尿布,橙汁 |
在5条交易记录中{尿布, 啤酒}总共出现了3次,所以
S u p p o r t ( 尿布,啤酒 ) = 3 5 = 0.60.6 Support\left( \text{尿布,啤酒} \right) =\frac{3}{5}=0.60.6 Support(尿布,啤酒)=53=0.60.6
同样的,分析关联性的数据X,Y和Z(3-项集),则对应的支持度为:
S u p p o r t ( X , Y , Z ) = P ( X Y Z ) = n u m b e r ( X Y Z ) n u m ( A l l S a m p l e s ) Support\left( X,Y,Z \right) =P\left( XYZ \right) =\frac{number\left( XYZ \right)}{num\left( AllSamples \right)} Support(X,Y,Z)=P(XYZ)=num(AllSamples)number(XYZ)
一般来说,支持度高的数据不一定构成频繁项集,但是支持度太低的数据肯定不构成频繁项集。 另外,支持度是针对项集来说的,因此,可以定义一个最小支持度,而只保留满足最小支持度的项集,起到一个项集过滤的作用。
置信度(confidence)体现了一个数据出现后,另一个数据出现的概率,或者说数据的条件概率。
分析关联性的数据X和Y,X对Y的置信度为
C o n f i d e n c e ( X → Y ) = P ( Y ∣ X ) = P ( X Y ) P ( X ) Confidence\left( X\rightarrow Y \right) =P\left( Y|X \right) =\frac{P\left( XY \right)}{P\left( X \right)} Confidence(X→Y)=P(Y∣X)=P(X)P(XY)
例
交易号码 | 商品 |
---|---|
001 | 豆奶,莴苣 |
002 | 莴苣,尿布,啤酒,甜菜 |
003 | 豆奶,尿布,啤酒,橙汁 |
004 | 莴苣,豆奶,尿布,啤酒 |
005 | 莴苣,豆奶,尿布,橙汁 |
莴苣对豆奶的置信度=豆奶和莴苣同时出现的概率/莴苣出现的概率,则有
C o n f i d e n c e ( 莴笋 → 豆奶 ) = P ( 豆奶*莴笋 ) P ( 莴笋 ) = 3 / 5 4 / 5 = 0.75 Confidence\left( \text{莴笋}\rightarrow \text{豆奶} \right) =\frac{P\left( \text{豆奶*莴笋} \right)}{P\left( \text{莴笋} \right)}=\frac{3/5}{4/5}=0.75 Confidence(莴笋→豆奶)=P(莴笋)P(豆奶*莴笋)=4/53/5=0.75
同样的,对于三个数据X,Y,Z,则Y和Z对于X的置信度为:
C o n f i d e n c e ( Y Z → X ) = P ( X ∣ Y Z ) = P ( X Y Z ) P ( Y Z ) Confidence\left( YZ\rightarrow X \right) =P\left( X|YZ \right) =\frac{P\left( XYZ \right)}{P\left( YZ \right)} Confidence(YZ→X)=P(X∣YZ)=P(YZ)P(XYZ)
为什么使用支持度和置信度?
支持度是一种重要度量,因为支持度很低的规则可能只是偶然出现。从商务角度来看,
低支持度的规则多半也是无意义的,因为对顾客很少同时购买的商品进行促销可能并无益处。因此,支持度通常用来删去那些无意义的规则。此外,支持度还具有一种期望的性质,可以用于关联规则的有效发现。
另一方面,置信度衡量通过规则进行推理具有可靠性。对于给定的规则X→Y,置信度越高,Y在包含X的事务中出现的可能性就越大。置信度也可以估计Y在给定X下的条件概率。
同时,应当小心解释关联分析的结果。由关联规则作出的推论并不必然蕴涵因果关系。它只表示规则前件和后件同时出现的一种概率。
⽀持度和置信度是有局限性的,现有的关联规则的挖掘算法需要使⽤⽀持度和置信度来除去没有意义的模式。
⽀持度的缺点在于许多潜在的有意义的模式由于包含⽀持度小的项而被删去,置信度的缺点在于置信度度量会忽略规则后件中出现的项集的⽀持度,高置信度的规则有时可能出现误导。
解决这个问题的⼀种⽅法是使⽤称作提升度(lift)的度量:
l i f t ( X → Y ) = c ( X → Y ) s ( Y ) lift\left( X\rightarrow Y \right) =\frac{c\left( X\rightarrow Y \right)}{s\left( Y \right)} lift(X→Y)=s(Y)c(X→Y)
提升度表示 X → Y X\rightarrow Y X→Y 的置信度与后件Y的支持度之比,即:
l i f t ( X → Y ) = P ( Y ∣ X ) P ( Y ) = C o n f i d e n c e ( X → Y ) P ( Y ) lift\left( X\rightarrow Y \right) =\frac{P\left( Y|X \right)}{P\left( Y \right)}=\frac{Confidence\left( X\rightarrow Y \right)}{P\left( Y \right)} lift(X→Y)=P(Y)P(Y∣X)=P(Y)Confidence(X→Y)
提升度体现了X和Y之间的关联关系,
一个特殊的情况,如果X和Y独立,则有 l i f t ( X → Y ) = 1 lift\left( X\rightarrow Y \right) =1 lift(X→Y)=1
一般来说,要选择一个数据集合中的频繁数据集,则需要自定义评估标准。最常用的评估标准是用自定义的支持度,或者是自定义支持度和置信度的一个组合。
最小支持度(minsup)和最小置信度(minconf)
给定事务的集合T,关联规则发现是指找出支持度大于等于 m i n s u p minsup minsup 并且置信度大于等于 m i n c o n f minconf minconf 的所有规则,其中 m i n s u p minsup minsup 和 m i n c o n f minconf minconf 是对应的支持度和置信度阈值。
挖掘关联规则的一种原始方法是:计算每个可能规则的支持度和置信度。但是这种方法的代价很高,因为可以从数据集提取的规则的数目达指数级。
为了避免进行不必要的计算,事先对规则剪枝,而无须计算它们的支持度和置信度的值将是有益的。 因此,大多数关联规则挖掘算法通常采用的一种策略是,将关联规则挖掘任务分解为如下两个主要的子任务。
通常,频繁项集产生所需的计算开销远大于产生规则所需的计算开销。那有没有办法可以减少这种无用的计算呢?
有。下面这两种方法可以降低产生频繁项集的计算复杂度:
(1)减少候选项集的数目M。
(2)减少比较次数。替代将每个候选项集与每个事务相匹配,可以使用更高级的数据结构,或者存储候选项集或者压缩数据集,来减少比较次数。
|
逐一的遍历哪些集合出现次数最多,很麻烦。
如上图,四种商品就要遍历15次,所以为了降低所需的计算时间,研究人员发现一种所谓的Apriori原理。Apriori原理可以帮我们减少可能感兴趣的项集。Apriori原理是说如果某个项集是频繁的,那么它的所有子集也是频繁的,也即先验原理。
先验原理 :如果一个项集是频繁的,则它的所有子集一定也是频繁的。
根据先验原理,假如 {1,2,3} 是频繁项集,那么它的所有子集(下图中橘色项集)一定也是频繁的。
这个先验原理直观上并没有什么帮助,但是反过来看就有用了,也就是说如果一个项集是非频繁项集,那么它的所有超集也是非频繁的,如下所示:
上图中,已知阴影项集{2,3}是非频繁的。利用这个知识,我们就知道项集{0,2,3} ,{1,2,3}以及{0,1,2,3}也是非频繁的。这也就是说,一旦计算出了{2,3}的支持度,知道它是非频繁的之后,就不需要再计算{0,2,3}、{1,2,3}和{0,1,2,3}的支持度,因为我们知道这些集合不会满足我们的要求。
使用该原理就可以避免项集数目的指数增长,从而在合理时间内计算出频繁项集。
Apriori算法的主要思想是找出存在于事务数据集中的最大的频繁项集,在利用得到的最大频繁项集与预先设定的最小置信度阈值生成强关联规则。
频繁项集的所有非空子集也必须是频繁项集。根据该性质可以得出:向不是频繁项集 I 的项集中添加事务A,新的项集 I∪A 一定也不是频繁项集。
关联分析的目的包括两项:发现频繁项集和发现关联规则。首先需要找到频繁项集,然后才能获得关联规则。
过程一:找出所有的频繁项集
过程二:由频繁项集产生强关联规则
由过程一可知未超过预定的最小支持度阈值的项集已被剔除,如果剩下这些规则又满足了预定的最小置信度阈值,那么就挖掘出了强关联规则。
在过程一中需要连接步和剪枝步互相融合,最终得到最大频繁项集 。
(1)对给定的最小支持度阈值,分别对 1 项候选集 C 1 C_1 C1 ,剔除小于该阈值的的项集得到 1 项频繁集 L 1 L_1 L1;
(2)下一步由自身连接产生 2 项候选集 C 2 C_2 C2,保留 C 2 C_2 C2 中满足约束条件的项集得到 2 项频繁集,记为 L 2 L_2 L2 ;
(3)再下一步由 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。
剪枝步紧接着连接步,在产生候选项 C k C_k Ck的过程中起到减小搜索空间的目的。
由于 C k C_k Ck 是 L k − 1 L_{k-1} Lk−1与 L 1 L_1 L1连接产生的,根据Apriori 的性质频繁项集的所有非空子集也必须是频繁项集,所以不满足该性质的项集将不会存在于 C k C_k Ck ,该过程就是剪枝。
Apriori算法过程
发现频繁项集的过程如上图所示:
C1,C2,…,Ck分别表示1-项集,2-项集,…,k-项集;
L1,L2,…,Lk分别表示有k个数据项的频繁项集。
支持度过滤,满足最小支持度的项集才留下,不满足最小支持度的项集直接舍掉。
例,下面是一个超市的交易记录:
最小支持度为0.2,不满足这个概率的就会被剔除,然后又自身连接,又计算概率
下面我们用python代码来实现这一过程
函数1:创建一个用于测试的简单数据集
import pandas as pd
import numpy as np
def loadDataSet():
dataSet = [[1,3,4],[2,3,5],[1,2,3,5],[2,5]]
return dataSet
dataSet = loadDataSet()
dataSet
# [[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]]
函数2:构建第一个候选集合C1。
由于算法一开始是从输入数据集中提取候选项集列表,所以这里需要一个特殊的函数来处理——frozenset类型。
frozenset是指被“冰冻”的集合,也就是用户不能修改他们。这里必须要用frozenset而非set类型,是因为后面我们要将这些集合作为字典键值使用。
s = set('hello') #生成集合,集合会自动去重 {'e', 'h', 'l', 'o'}
s.add('a') #集合允许修改 {'a', 'e', 'h', 'l', 'o'}
f = frozenset('hello') #创建frozenset类型,也有去重功能
f.add('a') #frozdenset类型不允许修改
该函数流程是:首先创建一个空列表,用来储存所有不重复的项值。接下来遍历数据集中所有的交易记录,对每一条交易记录,遍历记录中的每一个项。
如果该项没有在C1中出现过,那么就把它添加到C1中,这里需要注意的是,并非简单地添加物品项,而是添加只包含该物品项的一个集合(此处用集合或者列表都可以)。循环完毕后,对整个C1进行排序并将其中每个单元素集合映射到frozenset(),最后返回frozenset的列表。
def createC1(dataSet):
C1 = [] #放置候选1项集
for transaction in dataSet: #数据集中每一条事务
for item in transaction: #事务中的每一个项集
if not {item} in C1:
C1.append({item})
C1.sort()
return list(map(frozenset, C1))
C1=createC1(dataSet)
'''
[frozenset({1}),
frozenset({3}),
frozenset({4}),
frozenset({2}),
frozenset({5})]
'''
函数3:生成满足最小支持度的频繁项集L1
C1是大小为1的所有候选项集的集合,Apriori算法首先构建集合C1,然后扫描数据集来判断这些只有一个元素的项集是否满足最小支持度的要求。那些满足最低要求的项集构成集合L1。
"""
函数功能:挑选满足最小支持度的项集,构成频繁项集
参数说明:
D:原始数据集
Ck:候选项集
minSupport:最小支持度
"""
def scanD(D, Ck, minSupport):
ssCnt = {} #字典的形式存放项集及其出现次数,{项集:次数}
for tid in D:
for can in Ck:
if can.issubset(tid): #判断can是否是tid的子集,返回的是布尔型数据
ssCnt[can] = ssCnt.get(can, 0) + 1 #如果是子集则字典值加1
# dict.get(key, default=None)
# get() 函数返回指定键的值,如果值不在字典中返回默认值。
numItems = float(len(D))
retList= []
supportData = {} #候选集项Ck的支持度字典(key:候选项, value:支持度)
for key in ssCnt:
support = ssCnt[key] / numItems
supportData[key] = support
if support >= minSupport:
retList.append(key)
return retList, supportData
L1, supportData = scanD(dataSet, C1, 0.5)
L1
#[frozenset({1}), frozenset({3}), frozenset({2}), frozenset({5})]
supportData
'''
frozenset({1}): 0.5,
frozenset({3}): 0.75,
frozenset({4}): 0.25,
frozenset({2}): 0.75,
frozenset({5}): 0.75}
'''
从运行结果中可以看出,设定最小支持度为0.5时,frozenset({4})支持度为0.25<0.5,就被舍弃了,没有放入到L1中。
根据前述Apriori算法工作流程,我们已经有了频繁一项集的生成函数,但是我们还缺少由频繁k项集生成候选k+1项集的函数,下面我们就来构建这个函数。
构建这个函数的思路是:将不同的频繁k项集两两合并生成候选k+1项集。
在这个函数的构造过程中,有两点需要注意:
运算符 | 在python中表示集合的合并,frozenset集合也可以执行合并操作,并且合并结果仍为frozenset类型。
项集迭代函数
#连接操作,将频繁Lk项集通过拼接转换为候选k+1项集
def aprioriGen(Lk):
Ck = [] #放置候选k+1项集
lenLk = len(Lk) #频繁k项集的长度
for i in range(lenLk):
for j in range(i+1, lenLk):
L1 = Lk[i]
L2 = Lk[j]
C = Lk[i] | Lk[j]
if not C in Ck and (len(C)==len(Lk[0])+1):
Ck.append(Lk[i] | Lk[j])
return Ck
由前面运行结果得知L1中共有4个频繁1项集,那两两组合的话,可以得到 个候选2项集。现在我们用aprioriGen函数由频繁1项集生成候选2项集,查看运行结果:
频繁1项集 L 1 L_1 L1
L1
# [frozenset({1}), frozenset({3}), frozenset({2}), frozenset({5})]
调用函数,得到候选集 C 2 C_2 C2
C2 = aprioriGen(L1)
C2
'''
[frozenset({1, 3}),
frozenset({1, 2}),
frozenset({1, 5}),
frozenset({2, 3}),
frozenset({3, 5}),
frozenset({2, 5})]
'''
由候选2项集 C 2 C_2 C2 生成频繁2项集 L 2 L_2 L2
#生成频繁2项集
L2, supportData= scanD(dataSet, C2, 0.5)
L2
# [frozenset({1, 3}), frozenset({2, 3}), frozenset({3, 5}), frozenset({2, 5})]
supportData
'''
{frozenset({1, 3}): 0.5,
frozenset({2, 3}): 0.5,
frozenset({3, 5}): 0.5,
frozenset({2, 5}): 0.75,
frozenset({1, 2}): 0.25,
frozenset({1, 5}): 0.25}
'''
接下来可以看一下由频繁2项集 L 2 L_2 L2 生成候选3项集 C 3 C_3 C3
#由频繁二项集生成候选3项集
C3 = aprioriGen(L2)
C3
# [frozenset({1, 2, 3}), frozenset({1, 3, 5}), frozenset({2, 3, 5})]
并由候选3项集 C 3 C_3 C3 生成频繁3项集 L 3 L_3 L3 的过程…,得到最大频繁项集。
L3, supportData= scanD(dataSet, C3, 0.5)
L3
# [frozenset({2, 3, 5})]
supportData
'''
{frozenset({2, 3, 5}): 0.5,
frozenset({1, 2, 3}): 0.25,
frozenset({1, 3, 5}): 0.25}
'''
总的来说,就是
(1)对给定的最小支持度阈值,分别对 1 项候选集 C 1 C_1 C1 ,剔除小于该阈值的的项集得到频繁 1 项集 L 1 L_1 L1;
(2)下一步由自身连接产生 2 项候选集 C 2 C_2 C2,保留 C 2 C_2 C2 中满足约束条件的项集得到频繁 2 项集,记为 L 2 L_2 L2 ;
(3)再下一步由 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。
函数2:根据数据集和支持度,返回所有的频繁项集,以及所有项集的支持度。
"""
Apriori主函数:根据输入数据集,返回所有频繁项集和所有项集及其支持度
参数说明:
D:原始数据集
minSupport:最小支持度
返回:
L:所有频繁项集
supportData:所有项集及其支持度
"""
def apriori(D, minSupport = 0.5):
C1 = createC1(D) #生成候选1项集C1
L1, supportData = scanD(D, C1, minSupport) #生成频繁1项集和支持度列表
L = [L1] #先把频繁1项集放入L
k=2 #项集数,初始值设为2
while (len(L[-1]) > 0): #只要最后一个频繁项集不为空则持续循环
Ck = aprioriGen(L[-1]) #生成候选k项集Ck
Lk, supK = scanD(D, Ck, minSupport) #支持度过滤,生成频繁k项集及支持度列表
supportData.update(supK) #更新支持度列表
L.append(Lk) #更新频繁项集列表
k=k+1 #更新循环因子
return L, supportData
调用
dataset = loadDataSet() #创建数据集
# [[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]]
L, supportData = apriori(dataset, minSupport=0.5)
'''
[[frozenset({1}), frozenset({3}), frozenset({2}), frozenset({5})],
[frozenset({1, 3}), frozenset({2, 3}), frozenset({3, 5}), frozenset({2, 5})],
[frozenset({2, 3, 5})], []]
'''
supportData
'''
{frozenset({1}): 0.5,
frozenset({3}): 0.75,
frozenset({4}): 0.25,
frozenset({2}): 0.75,
frozenset({5}): 0.75,
frozenset({1, 3}): 0.5,
frozenset({2, 3}): 0.5,
frozenset({3, 5}): 0.5,
frozenset({2, 5}): 0.75,
frozenset({1, 2}): 0.25,
frozenset({1, 5}): 0.25,
frozenset({2, 3, 5}): 0.5,
frozenset({1, 2, 3}): 0.25,
frozenset({1, 3, 5}): 0.25}
'''
完成频繁项集的挖掘之后,我们就可以根据这些频繁项集来寻找关联规则了。
对于频繁项集,上面给出了量化定义,即它满足最小支持度要求。那么,对于关联规则,我们也有类似的量化方法——置信度。
对于一条规则P→H的置信度可以表示为:
s u p p o r t ( H ∣ P ) / s u p p o r t ( P ) support\left( H|P \right) /support\left( P \right) support(H∣P)/support(P)
类似于频繁项集的生成,每一个频繁项集也可以生成许多条关联规则。如下图,列出了频繁项集{0,1,2,3}可以生成的全部规则。如果某一条规则不满足最小置信度要求,那么该规则的所有子集也不会满足最小置信度的要求(如图中阴影部分所示)。
我们可以利用这条性质来减少需要计算的规则数目,一旦发现某条规则不满足最小置信度的要求,那么这条规则的所有子集也就不用再去计算了。
挖掘关联规则的流程为:首先从一个频繁项集开始,创建一个规则列表,其中规则右部只包含一个元素,然后计算这些规则的置信度。接下来合并所有剩余规则,创建一个新的规则列表,其中右部包含两个元素,计算这些规则的置信度……如此循环,遍历所有的规则。这种方法也被称为分级法 。
了解流程之后,我们尝试用python实现这一流程。
在上面的代码结果中,
dataset = loadDataSet() #创建数据集
# [[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]]
L, supportData = apriori(dataset, minSupport=0.5)
'''
[[frozenset({1}), frozenset({3}), frozenset({2}), frozenset({5})],
[frozenset({1, 3}), frozenset({2, 3}), frozenset({3, 5}), frozenset({2, 5})],
[frozenset({2, 3, 5})],
[]]
'''
supportData
'''
{frozenset({1}): 0.5,
frozenset({3}): 0.75,
frozenset({4}): 0.25,
frozenset({2}): 0.75,
frozenset({5}): 0.75,
frozenset({1, 3}): 0.5,
frozenset({2, 3}): 0.5,
frozenset({3, 5}): 0.5,
frozenset({2, 5}): 0.75,
frozenset({1, 2}): 0.25,
frozenset({1, 5}): 0.25,
frozenset({2, 3, 5}): 0.5,
frozenset({1, 2, 3}): 0.25,
frozenset({1, 3, 5}): 0.25}
'''
其中,L是一个由频繁项集组成的list,L中有频繁1项集、频繁2项集、频繁3项集、空集
而supportData则包含了所有项集及其支持度,supportData中有候选1项集、候选2项集、候选3项集。
对于L而言,第一个元素就是频繁一项集的list,第二个元素就是频繁二项集的list,以此类推。大家已经知道,置信度表示的是一个数据出现后另一个数据出现的概率(即条件概率),所以对于频繁一项集而言并无置信度的概念。频繁二项集的置信度相较于频繁
多项集(三项集及以上)更为简单,因此在计算置信度时,我们对频繁二项集和频繁多项集分别计算。
首先考虑频繁二项集,返回的L中包含了所有的频繁项集,直接取出频繁二项集L[1]
#提取出频繁二项集
L2=L[1]
L2
'''
[frozenset({1, 3}), frozenset({2, 3}), frozenset({3, 5}), frozenset({2, 5})]
'''
#以频繁二项集里面第一个元素为例,计算规则1→3的置信度
freqSet = L2[0]
freqSet # frozenset({1, 3})
#置信度的计算过程非常简单,借助集合运算以及Apriori的supportData计算结果,
#用频繁项集的支持度除以前驱项的支持度即可,例如计算1→3的置信度:
#sup(13)/sup(1)
supportData[freqSet]/supportData[freqSet-frozenset({3})]
# 1.0
#3→1的置信度
supportData[freqSet]/supportData[freqSet-frozenset({1})]
# 0.6666666666666666
将上述过程封装成函数:
"""
函数功能:根据单个频繁项集生成满足最小置信度的关联规则
参数说明:
freqSet:单个频繁项集,比如频繁2项集、频繁3项集
H:可以出现在关联规则右部的元素列表
supportData:所有项集及其支持度
brl:强关联规则列表
minConf:最小置信度
返回:
prunedH:关联规则右部元素列表
"""
def calcConf(freqSet, H, supportData, brl, minConf=0.7):
prunedH = [] #放置置信度过滤后的可以出现在规则右部的元素列表,如1→3,存放3
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)) #把强关联规则追加到 brl
prunedH.append(conseq) #将经过置信度过滤的元素,放入prunedH
return prunedH
频繁2项集 {1, 3}
freqSet # frozenset({1, 3})
brl=[]
H1 = [frozenset([item]) for item in freqSet]
H1 # [frozenset({1}), frozenset({3})]
#调用
calcConf(freqSet, H1, supportData, brl, minConf=0.5)
'''
frozenset({3}) --> frozenset({1}) conf: 0.6666666666666666
frozenset({1}) --> frozenset({3}) conf: 1.0
[frozenset({1}), frozenset({3})]
'''
频繁2项集 {3,5}
freqSet = L[1][2] # frozenset({3, 5})
brl=[]
H1 = [frozenset([item]) for item in freqSet]
calcConf(freqSet, H1, supportData, brl, minConf=0.5)
'''
frozenset({5}) --> frozenset({3}) conf: 0.6666666666666666
frozenset({3}) --> frozenset({5}) conf: 0.6666666666666666
[frozenset({3}), frozenset({5})]
'''
频繁多项集生成函数
对于多项集,生成置信度规则的过程会更加复杂,在上述例子中,只有一个频繁三项集{2,3,5},接下来考虑计算该项集所能生成的规则置信度。首先仍然是将该频繁项集赋值给一个单独的变量,然后将其内部的每个元素单独保存为一个frozenset。
频繁3项集的规则生成函数
def rulesFromConseq(freqSet, H, supportData, brl, minConf=0.7):
Hmp = True #循环因子
while Hmp:
Hmp = False #改变循环因子
H = calcConf(freqSet, H, supportData, brl, minConf) #置信度过滤
H = aprioriGen(H) #把可以出现在规则右部的元素两两组合
Hmp = not(H == [] or len(H[0]) == len(freqSet)) #判断是否进入下一次循环
调用
#提取频繁三项集
freqSet = L[2][0]
freqSet
# frozenset({2, 3, 5})
H2 = [frozenset([item]) for item in freqSet]
H2
# [frozenset({2}), frozenset({3}), frozenset({5})]
brl = []
rulesFromConseq(freqSet, H2, supportData, brl, minConf=0.5)
'''
frozenset({3, 5}) --> frozenset({2}) conf: 1.0
frozenset({2, 5}) --> frozenset({3}) conf: 0.6666666666666666
frozenset({2, 3}) --> frozenset({5}) conf: 1.0
frozenset({5}) --> frozenset({2, 3}) conf: 0.6666666666666666
frozenset({3}) --> frozenset({2, 5}) conf: 0.6666666666666666
frozenset({2}) --> frozenset({3, 5}) conf: 0.6666666666666666
'''
生成关联规则的主函数
"""
函数功能:生成所有满足最小置信度的关联规则
参数说明:
L:频繁项集
supportData:所有项集及其支持度
minConf:最小置信度
返回:
bigRuleList:满足最小置信度的关联规则(即强关联规则)
"""
def generateRules(L, supportData, minConf=0.7):
bigRuleList = [] #初始化列表
for i in range(1, len(L)): #从1开始,是因为只有频繁二项集及以上才会有关联规则
for freqSet in L[i]: #对频繁k项集列表中的每一个频繁k项集进行关联规则挖掘
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
例,前面通过最小支持度过滤得出的 L 以及 supportData
dataset = loadDataSet() #创建数据集
# [[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]]
L, supportData = apriori(dataset, minSupport=0.5)
'''
[[frozenset({1}), frozenset({3}), frozenset({2}), frozenset({5})],
[frozenset({1, 3}), frozenset({2, 3}), frozenset({3, 5}), frozenset({2, 5})],
[frozenset({2, 3, 5})],
[]]
'''
supportData
'''
{frozenset({1}): 0.5,
frozenset({3}): 0.75,
frozenset({4}): 0.25,
frozenset({2}): 0.75,
frozenset({5}): 0.75,
frozenset({1, 3}): 0.5,
frozenset({2, 3}): 0.5,
frozenset({3, 5}): 0.5,
frozenset({2, 5}): 0.75,
frozenset({1, 2}): 0.25,
frozenset({1, 5}): 0.25,
frozenset({2, 3, 5}): 0.5,
frozenset({1, 2, 3}): 0.25,
frozenset({1, 3, 5}): 0.25}
'''
调用关联规则的主函数
brl = generateRules(L, supportData, minConf=0.5)
'''
frozenset({3}) --> frozenset({1}) conf: 0.6666666666666666
frozenset({1}) --> frozenset({3}) conf: 1.0
frozenset({3}) --> frozenset({2}) conf: 0.6666666666666666
frozenset({2}) --> frozenset({3}) conf: 0.6666666666666666
frozenset({5}) --> frozenset({3}) conf: 0.6666666666666666
frozenset({3}) --> frozenset({5}) conf: 0.6666666666666666
frozenset({5}) --> frozenset({2}) conf: 1.0
frozenset({2}) --> frozenset({5}) conf: 1.0
frozenset({3, 5}) --> frozenset({2}) conf: 1.0
frozenset({2, 5}) --> frozenset({3}) conf: 0.6666666666666666
frozenset({2, 3}) --> frozenset({5}) conf: 1.0
frozenset({5}) --> frozenset({2, 3}) conf: 0.6666666666666666
frozenset({3}) --> frozenset({2, 5}) conf: 0.6666666666666666
frozenset({2}) --> frozenset({3, 5}) conf: 0.6666666666666666
'''
主要需要两个主函数,
所有定义的函数
"""
Apriori主函数:根据输入数据集,返回所有频繁项集和所有项集及其支持度
参数说明:
输入:
D:原始数据集
minSupport:最小支持度
minConf:最小置信度
返回:
L:所有频繁项集
supportData:所有项集及其支持度
bigRuleList:满足最小置信度的关联规则(即强关联规则)
"""
def createC1(dataSet):
C1 = [] #放置候选1项集
for transaction in dataSet:
#print(transaction)
for item in transaction:
#print(item)
if not {item} in C1:
#print({item})
C1.append({item})
#print(C1)
C1.sort()
return list(map(frozenset, C1))
def scanD(D, Ck, minSupport):
ssCnt = {}
for tid in D:
for can in Ck:
if can.issubset(tid): #判断can是否是tid的子集,返回的是布尔型数据
ssCnt[can] = ssCnt.get(can, 0) + 1
numItems = float(len(D))
retList= []
supportData = {} #候选集项Ck的支持度字典(key:候选项, value:支持度)
for key in ssCnt:
support = ssCnt[key] / numItems
supportData[key] = support
if support >= minSupport:
retList.append(key)
return retList, supportData
#连接操作,将频繁Lk项集通过拼接转换为候选k+1项集
def aprioriGen(Lk):
Ck = [] #放置候选k+1项集
lenLk = len(Lk) #频繁k项集的长度
for i in range(lenLk):
for j in range(i+1, lenLk):
L1 = Lk[i]
L2 = Lk[j]
C = Lk[i] | Lk[j]
if not C in Ck and (len(C)==len(Lk[0])+1):
Ck.append(Lk[i] | Lk[j])
return Ck
def calcConf(freqSet, H, supportData, brl, minConf=0.7):
prunedH = [] #放置置信度过滤后的可以出现在规则右部的元素列表,如1→3,存放3
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)) #把强关联规则追加到 brl
prunedH.append(conseq) #将经过置信度过滤的元素,放入prunedH
return prunedH
#频繁3项集的规则生成函数
def rulesFromConseq(freqSet, H, supportData, brl, minConf=0.7):
Hmp = True #循环因子
while Hmp:
Hmp = False #改变循环因子
H = calcConf(freqSet, H, supportData, brl, minConf) #置信度过滤
H = aprioriGen(H) #把可以出现在规则右部的元素两两组合
Hmp = not(H == [] or len(H[0]) == len(freqSet)) #判断是否进入下一次循环
def apriori(D, minSupport = 0.5, minConf=0.7):
C1 = createC1(D) #生成候选1项集C1
L1, supportData = scanD(D, C1, minSupport) #生成频繁1项集和支持度列表
L = [L1] #先把频繁1项集放入L
k=2 #项集数,初始值设为2
while (len(L[-1]) > 0): #只要最后一个频繁项集不为空则持续循环
Ck = aprioriGen(L[-1]) #生成候选k项集Ck
Lk, supK = scanD(D, Ck, minSupport) #支持度过滤,生成频繁k项集及支持度列表
supportData.update(supK) #更新支持度列表
L.append(Lk) #更新频繁项集列表
k=k+1 #更新循环因子
return L, supportData
def generateRules(L, supportData, minConf=0.7):
bigRuleList = [] #初始化列表
for i in range(1, len(L)): #从1开始,是因为只有频繁二项集及以上才会有关联规则
for freqSet in L[i]: #对频繁k项集列表中的每一个频繁k项集进行关联规则挖掘
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
例
读取外部数据进行关联规则挖掘
gr = open('C:/groceries.txt')
gro = gr.readlines()
gro
'''
['bread milk\n',
'bread diaper beer egg\n',
'milk diaper beer coke\n',
'bread milk diaper beer\n',
'bread milk diaper coke']
'''
tran = [gro[i].strip('\n').split(' ') for i in range(len(gro))]
'''
[['bread', 'milk'],
['bread', 'diaper', 'beer', 'egg'],
['milk', 'diaper', 'beer', 'coke'],
['bread', 'milk', 'diaper', 'beer'],
['bread', 'milk', 'diaper', 'coke']]
'''
调用函数
L, supportData = apriori(tran, minSupport = 0.5)#频繁项集的挖掘
L
'''
[[frozenset({'bread'}),
frozenset({'milk'}),
frozenset({'diaper'}),
frozenset({'beer'})],
[frozenset({'bread', 'milk'}),
frozenset({'bread', 'diaper'}),
frozenset({'beer', 'diaper'}),
frozenset({'diaper', 'milk'})],
[]]
'''
supportData
'''
{frozenset({'bread'}): 0.8,
frozenset({'milk'}): 0.8,
frozenset({'diaper'}): 0.8,
frozenset({'beer'}): 0.6,
frozenset({'egg'}): 0.2,
frozenset({'coke'}): 0.4,
frozenset({'bread', 'milk'}): 0.6,
frozenset({'bread', 'diaper'}): 0.6,
frozenset({'beer', 'bread'}): 0.4,
frozenset({'beer', 'diaper'}): 0.6,
frozenset({'diaper', 'milk'}): 0.6,
frozenset({'beer', 'milk'}): 0.4,
frozenset({'beer', 'bread', 'diaper'}): 0.4,
frozenset({'beer', 'diaper', 'milk'}): 0.4,
frozenset({'bread', 'diaper', 'milk'}): 0.4}
'''
brl = generateRules(L, supportData, minConf=0.5) #挖掘关联规则
'''
frozenset({'bread'}) --> frozenset({'milk'}) conf: 0.7499999999999999
frozenset({'milk'}) --> frozenset({'bread'}) conf: 0.7499999999999999
frozenset({'bread'}) --> frozenset({'diaper'}) conf: 0.7499999999999999
frozenset({'diaper'}) --> frozenset({'bread'}) conf: 0.7499999999999999
frozenset({'beer'}) --> frozenset({'diaper'}) conf: 1.0
frozenset({'diaper'}) --> frozenset({'beer'}) conf: 0.7499999999999999
frozenset({'milk'}) --> frozenset({'diaper'}) conf: 0.7499999999999999
frozenset({'diaper'}) --> frozenset({'milk'}) conf: 0.7499999999999999
'''