关联规则挖掘(Apriori算法实现)
关联规则挖掘:是一种在大型数据库中发现变量之间的有趣性关系的方法。它的目的是利用一些有趣性的量度来识别数据库中发现的强规则。基于强规则的概念,Rakesh Agrawal等人[引入了关联规则以发现由超市的pos系统记录的大批交易数据中产品之间的规律性。例如,从销售数据中发现的规则:在消费者去超市购物时,假设消费者在本次的消费过程中买了牛奶,那他本次消费中买面包的概率也很大,此类信息可以作为做出促销定价或产品销售等营销活动决定的根据。
Apriori算法是常用的用于挖掘出数据关联规则的算法,它用来找出数据值中频繁出现的数据集合,找出这些集合的模式有助于我们做一些决策。
下面举一个具体的例子来说明问题:
TID | 项集ID列表 |
1 | 牛奶,面包,鸡蛋 |
2 | 牛奶,面包 |
3 | 牛奶,奶酪 |
4 | 键盘,鼠标,鸡蛋 |
上面是四位消费者在一次购物过程中购买的账单流水,可以看到{牛奶}在四次购物中出现了三次,{面包},在四次购物中出现了两次,{牛奶,面包}在四次购物中出现了两次,而换个角度来考虑,当消费者购买了牛奶过后,那他买面包的概率即为三分之二;而当消费者确定买了面包后,其在本次购物中购买牛奶的概率为100%!这是不是很有意思。
接下来我们引入一些概念:
(1)支持度:支持度即为几个关联的数据在数据集中出现的次数占总数据集次数的比重
例如上面的例子,{牛奶,面包}得支持度为 support({牛奶,面包})={牛奶,面包}出现的次数/总次数=2/4=0.5
(2)置信度:置信度是指一个数据出现后,另一个数据出现的概率。其实换种角度来想,即为条件概率。
例如上面的例子,牛奶购买后,再买面包的概率为2/3,面包购买后,再买牛奶的概率为1
(3)频繁项集:关联数据出现的次数大于等于给定阈值(如置信度、支持度)项集称为频繁项集
实战演练:
上面的例子中展示了如何在指定支持度和置信度的条件下完成频繁项集的挖掘,下面是完整的程序代码:
from numpy import *
import itertools
support_dic = {}
#生成原始数据,用于测试
def loadDataSet():
return [[11,12,15], [11,12], [12,14], [11,12,14], [11,13], [11,12,13,15], [11,12,13], [12,15], [12,13,14], [13,14]] #为便于处理,将I1用11替换,以此类推
#获取整个数据库中的一阶元素
def createC1(dataSet):
C1 = set([])
for item in dataSet:
C1 = C1.union(set(item))
return [frozenset([i]) for i in C1]
#输入数据库(dataset) 和 由第K-1层数据融合后得到的第K层数据集(Ck),
#用最小支持度(minSupport)对 Ck 过滤,得到第k层剩下的数据集合(Lk)
def getLk(dataset, Ck, minSupport):
global support_dic
Lk = {}
#计算Ck中每个元素在数据库中出现次数
for item in dataset:
for Ci in Ck:
if Ci.issubset(item):
if not Ci in Lk:
Lk[Ci] = 1
else:
Lk[Ci] += 1
#用最小支持度过滤
Lk_return = []
for Li in Lk:
support_Li = Lk[Li] / float(len(dataSet))
if support_Li >= minSupport:
Lk_return.append(Li)
support_dic[Li] = support_Li
return Lk_return
#将经过支持度过滤后的第K层数据集合(Lk)融合
#得到第k+1层原始数据Ck1
def genLk1(Lk):
Ck1 = []
for i in range(len(Lk) - 1):
for j in range(i + 1, len(Lk)):
if sorted(list(Lk[i]))[0:-1] == sorted(list(Lk[j]))[0:-1]:
Ck1.append(Lk[i] | Lk[j])
return Ck1
#遍历所有二阶及以上的频繁项集合
def genItem(freqSet, support_dic):
for i in range(1, len(freqSet)):
for freItem in freqSet[i]:
genRule(freItem)
#输入一个频繁项,根据“置信度”生成规则
#采用了递归,对规则树进行剪枝
def genRule(Item, minConf=0.8):
if len(Item) >= 2:
for element in itertools.combinations(list(Item), 1):
if support_dic[Item] / float(support_dic[Item - frozenset(element)]) >= minConf:
print (str([Item - frozenset(element)]) + "----->" + str(element))
print (support_dic[Item] / float(support_dic[Item - frozenset(element)]))
genRule(Item - frozenset(element))
#输出结果
if __name__ == '__main__':
dataSet = loadDataSet()
result_list = []
Ck = createC1(dataSet)
#循环生成频繁项集合,直至产生空集
while True:
Lk = getLk(dataSet, Ck, 0.5)
if not Lk:
break
result_list.append(Lk)
Ck = genLk1(Lk)
if not Ck:
break
#输出频繁项及其“支持度”
print (support_dic)
#输出规则
genItem(result_list,support_dic)
实验代码运行结果:
{frozenset({11}): 0.6, frozenset({12}): 0.8, frozenset({13}): 0.5, frozenset({11, 12}): 0.5}
frozenset({11}) --> frozenset({12}) 0.8333333333333334
frozenset({15}) --> frozenset({12}) 1.0