通过查看哪些商品经常在一起购买,可以帮助商店了解用户的购买行为。这种从数据海洋中抽取的知识可以用于商品定价,市场促销,存货管理等环节。从大规模数据集中寻找物品的隐含关系称为 关联分析 或则 关联规则学习。我们将使用Apriori算法来解决上述问题。
下面首先讨论关联分析,再讨论apriori原理,apriori算法正是基于该原理得到的。
1. 关联分析
关联分析是一种在大规模数据集中寻找有趣关系的任务。 这些关系可以有两种形式:频繁项集或则关联规则。 频繁项集是经常出现在一块的物品的集合, 关联规则暗示两种物品之间可能存在很强的关系。
频繁项集是指那些经常出现在一起的物品集合,比如{葡萄酒,尿布,豆奶}就是频繁项集的一个例子。从下面的数据集中也可以看出尿布--->葡萄酒的关联规则。这就意味着如果有人买了尿布,那么他也可能会买葡萄酒。
一个项集的支持度被定义为数据集中包含该项集的记录所占的比例。 上图中{豆奶}的支持度为4/5。五条交易记录中有三条包含{豆奶,尿布},因此{豆奶,尿布}的支持度是3/5。支持度是针对项集来说的,因此可以定义一个最小支持度,而只保留满足最小支持度的项集。
可信度或置信度是针对一条诸如{尿布}-->{葡萄酒}的关联规则来定义的。这条规则的置信度被定义为“支持度{尿布,葡萄酒}/支持度{尿布}”。从图11-1中可以计算出{尿布,葡萄酒}的支持度为3/5,尿布的支持度为4/5,所以“{尿布}-->{葡萄酒}” 的可信度为3/4=0.75。这以为着对于包含“尿布”的所有记录中,我们的规则对其中75%的记录都适用。
支持度和可信度是用来量化关联分析是否成功的方法。假设想寻找支持度大于0.8的所有项集,一个办法是生成一个物品所有可能组合的清单,然后对每一种组合统计它出现的频繁程度,但是当物品成千上万时,上述做法会非常慢。所以我们将会学习aprior原理,该原理将会减少关联规则学习时所需的计算量。
1.2 Apriori原理
假设我们经营一家商品种类并不多的杂货店,我们我们对那些经常在一起被购买的商品非常感兴趣。我们只有4种商品:商品0,商品1,商品2和商品3。那么所有可能被一起购买的商品组合都有哪些?这些商品组合可能只有一种商品,比如商品0 , 也可能包括两种、三种或者所有四种商品。我们并不关心某人买了两件商品0以及四件商品2的情况,我们只关心他购买了一种或多种商品。
上图显示了物品之间所有可能的组合。物品集合之间的连线表明两个或更多的集合可以组合成一个更大的集合。
我们前面说过我们的目标是找到经常一起购买的物品集合,就要找到集合的支持度来度量其出现的频率。但是我们计算过程中要遍历数据很多次,在物品数目种类过大时会有更多的物品组合,对于现代计算机来说也需要很长时间才能运算完成。
apriori原理可以帮我们减少可能感兴趣的项集。apriori原始是说,如果某个项集是频繁的,那么它的所有子集也是频繁的。反过来说也就是,如果一个项集是非频繁项集,那么它所有的超集也是非频繁的。
1.3 使用Apriori算法来发现频繁项
关联分析的目标包括两项:发现频繁项集和发现关联规则。首先需要找到频繁项集,然后才能获得关联规则。本节将只关注与发现频繁项集。
apriori算法算是发现频繁项集的一种方法。apriori算法的两个输入参数分别是最小支持度和数据集。
该算法首先会生成所有单个物品的项集列表。接着扫描交易记录来查看哪些项集满足最小支持度要求,那些不满足最小支持度的集合会被去掉。然后 ,对剩下来的集合进行组合以生成包含两个元素的项集。接下来,再重新扫描交易记录,去掉不满足最小支持度的项集。该过程重复进行直到所有项集都被去掉。
1.3.1 生成候选项集
伪代码如下:
from numpy import *
def loadDataSet():
return [[1,3,4],
[2,3,5],
[1,2,3,5],
[2,5]]
# 大小为1的所有候选项项集的集合
# apriori算法首先构造集合C1
# 扫描数据集来判断这些只有一个元素的项集是否满足最小支持度
# 满足最低要求的项集构成集合L1.L1中的元素相互组合构成C2,C2再进一步过滤成L2...
def createC1(dataset):
C1 = []
for transaction in dataset:
for item in transaction:
# 如果某个物品不在C1中,添加只包含该物品项的一个列表
if not [item] in C1:
C1.append([item])
# 为每个物品项构建一个集合,一个由单物品列表组成的大列表
C1.sort()
# frozenset:用户不能修改的集合。
# 之后将这些集合作为字典的键值使用,使用fronzenset可以实现,set不可以
return map(frozenset, C1)
def scanD(D, Ck, minSuppotr):
'''
数据集,包含候选集合的列表,感兴趣项集最小支持度
用于从C1生成L1.
'''
ssCnt = {}
for tid in D: # 遍历所有交易记录
for can in Ck: # 遍历所有候选集
# 如果候选集是记录的一部分,增加字典对应的计数值,字典的键是集合
if can.issubset(tid):
if not ssCnt.has_key(can): ssCnt[can]=1
else: ssCnt[can] += 1
numItems = float(len(D))
retList = []
supportData = {}
# 计算支持度
for key in ssCnt:
support = ssCnt[key]/numItems
if support >= minSuppotr: # 大于最小支持度
retList.insert(0, key) # 频繁项
supportData[key] = support # 频繁项的支持度
return retList, supportData
查看运行结果:第一个后候选项集合C1
构建集合表示的数据集D
有了集合形式的数据,就可以去掉不满足最小支持度的项集。我们设为0.5
1.3.2 组织完整的apriori算法
def aprioriGen(Lk, k): # creates Ck
'''
频繁项集列表Lk,项集元素个数k
例如 输入{0}、{1}、{2}会生成{0,1}、{0,2}、{1,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]
if L1 == L2:
retList.append(Lk[i] | Lk[j] ) # 集合的并
return retList
def apriori(dataSet, minSupport = 0.5):
C1 = createC1(dataSet) # 创建C1
D = map(set, dataSet) # 转化为集合列表
L1, supportData = scanD(D, C1, minSupport)
L = [L1]
k = 2
while (len(L[k-2]) > 0):
Ck = aprioriGen(L[k-2], k)
Lk, supK = scanD(D, Ck, minSupport)#scan DB to get Lk
supportData.update(supK)
L.append(Lk)
k += 1
return L, supportData
运行结果:
L包含满足最小支持度为0.5的频繁项集列表,观察具体值:

这里6个集合是候选项Ck中的元素,其中四个集合在L[1]中,剩下的被过滤掉了。
1.3.3 从频繁项集中挖掘关联规则
人们最常寻找的两个目标是频繁项集与关联规则。上面我们介绍了如何寻找频繁项集,现在就来找出关联规则。
要找关联规则,首先要从一个频繁项集开始。我们知道集合中的元素是不重复的,但是我们想要知道基于这些元素能否获得其他内容。从某个元素或则元素集合推出另一个元素。
频繁项集的量化定义就是满足最下最小支持度的要求。对于关联规则我们也有类似的量化方法:可信度。
一条规则P--->H的可信度定义为support(P | H)/support(P)。前面已经计算出支持度了,现在取出那些支持度计算可信度。
我们可以观察到,如果某条规则并不满足最小可信度的要求,那么该规则所有子集也不会满足最小可信度要求。如下图,假设规则0.1.2-->3并不满足最小可信度要求,那么那么就知道左部为{0.1.2}子集的规则也不会满足最小可信度要求。
可以利用关联规则的上述性质来减少需要测试的数目规则。类似于之前的apriori算法,先从一个频繁项集开始,接着创建一个规则列表,其中规则右部只包括一个元素,然后对这些规则进行测试。接下来合并所有剩余规则来创建一个新的规则列表,其中规则右部包含两个元素。这种方法也称为分级法。
# 关联规则生成函数
def generateRules(L, supportData, minConf=0.7):
'''
输入:频繁项集列表,包含频繁项集支持数据的字典,最小可信度阈值
输出:包含可信度的规则列表
'''
bigRuleList = []
# 遍历L中的每一个频繁项集并对每个频繁项集创建只包含单个元素集合的列表H1
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
# 生成候选规则集合
def calcConf(freqSet, H, supportData, brl, minConf=0.7):
prunedH = [] #create new list to return
# 遍历所有项集并计算可信度
for conseq in H:
conf = supportData[freqSet]/supportData[freqSet-conseq] #calc confidence
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):
'''
频繁项集,可以出现在规则右边的元素列表H
'''
m = len(H[0])
if (len(freqSet) > (m + 1)): #try further merging
Hmp1 = aprioriGen(H, m+1)#create Hm+1 new candidates
Hmp1 = calcConf(freqSet, Hmp1, supportData, brl, minConf)
if (len(Hmp1) > 1): #need at least two sets to merge
rulesFromConseq(freqSet, Hmp1, supportData, brl, minConf)
运行结果:给出三条规则1-->3, 5-->2, 2-->5
降低可信度阈值之后:
一旦降低了可信度阈值就可以获得更多规则。
2. 本章小结
apriori原理是说如果一个元素项不是频繁的,那么包含该元素的超集也是不频繁的。apriori算法从单元项集开始,通过组合满足最小支持度要求的项集来形成更大的集合。
每次增加频繁项集的大小,apriori算法都会重新扫描整个数据集。当数据集很大时,这会显著降低频繁项集发现的速度。