使用Apriori算法进行关联分析

来源:互联网 发布:华为java面试题2014 编辑:程序博客网 时间:2024/05/03 14:54

现在商家通过查看哪些商品经常在一起购买,来了解用户的购买行为。这种从数据海洋中抽取的知识可以用于商品的定价、市场促销、存货管理等环节。从规模数据集中寻找物品间的隐含关系被称作关联分析或者关联规则学习。下面首先详细讨论关联分析,然后讨论Apriori原理,Apriori算法正式基于该原理得到的。接下来创建函数频繁项集高效发现的函数,然后从频繁项集中抽取出关联规则。

1.1 关联分析
关联分析是一种在大规模数据集中寻找有趣关系的任务。这些关系可以有两种形式:频繁项集或者关联规则频繁项集是经常出现在一起的物品的集合关联规则暗示两种物品之间可能存在很强的关系。例如经典的尿布—葡萄酒的关联规则,这意味着如果有人购买了尿布,那么他很可能也会买葡萄酒。使用频繁项集和关联规则,商家可以更好地了解他们的顾客。尽管大部分关联规则分析的实例来自零售业,但该技术同样可以用于其他行业,比如网站流量分析以及医药行业。
一个项集的支持度被定义为数据集中包含该项集的记录所占的比例。支持度是针对项集来说的,因此可以定义一个最小支持度,而只保留满足最小支持度的项集。
可信度或者置信度是针对一条诸如{尿布}—{葡萄酒}的关联规则来定义的。这条规则的可信度被定义为“支持度({尿布,葡萄酒})/支持度({尿布})”,这意味着对于包含“尿布”的所有记录中,我们的规则对其中75%的记录适用。

1.2 Apriori原理
要获得每种可能集合的支持度需要不断重复统计,而随着物品数的增加,遍历次数会急剧增长。为了降低所需的计算时间,研究人员提出Apriori原理。它可以帮我们减少可能感兴趣的项集。它说如果某个项集是频繁的,那么它的所有子集也是频繁项,这个原理直观上并没有什么帮助,但是如果反过来看就有用了,也就是说如果一个项集是非频繁集,那么它的所有超集也是非频繁的。

1.3 使用Apriori算法来发现频繁集
关联分析的目标包括两项:发现频繁项集和发现关联规则。首先需要找到频繁项集,然后才能获得关联规则。本节将只关注于发现频繁项集。
Apriori算法是发现频繁项集的一种方法。Apriori算法的两个输入参数分别是最小支持度和数据集。1.该算法首先会生成所有单个物品的项集列表,接着扫描交易记录来查看哪些项集满足最小支持度要求,哪些不满足最小支持度的集合会被去掉;2.然后对剩下来的集合进行组合以生成包含两个元素的项集,接着再重新扫描交易记录,去掉不满足最小支持度的项集。该过程重复进行直到所有项集都被去掉。

程序清单1-1:Apriori算法中的辅助函数

def loadDataSet():    return [[1,3,4],[2,3,5],[1,2,3,5],[2,5]]

第一个函数loadDataSet()创建了一个用于测试的简单数据集。

def creatC1(dataSet):         #创建C1候选项集    C1 = []    for transaction in dataSet:        for item in transaction:            if not [item] in C1:                C1.append([item])    C1.sort()    return map(frozenset,C1)

creatC1()将构建集合C1。C1是大小为1的所有候选项集的集合。Apriori算法首先构建集合C1,然后扫描数据集来判断这些只有一个元素的项集是否满足最小支持度的要求。那些满足最低要求的项集构成集合L1。而L1中的元素相互组合构成C2,C2再进一步过滤变为L2。
首先创建一个空列表C1,它用来存储所有不重复的项值。接下来遍历数据集中的所有交易记录。对每一个记录,遍历记录中的每一个项。如果某个物品项没有在C1中出现,则将其添加到C1中。这里并不是简单地添加每个物品项,而是添加只包含该物品项的一个列表。最后,对大列表进行排序并将其中的每个单元素列表映射到frozenset(),最后返回frozenset的列表。

def scanD(D,Ck,minSupport):      #生成满足最小支持度的频繁项集retList    ssCnt = {}                   #和每个项集对应的支持度的字典列表supportData    for tid in D:        for can in Ck:            if can.issubset(tid):   #测试是否can中的每一个元素都在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 >= minSupport:            retList.insert(0,key)        supportData[key] = support    return retList,supportData

scanD()有三个参数,分别是数据集、候选项集列表Ck以及感兴趣项集的最小支持度minSupport。该函数用于从C1生成L1。另外,该函数会返回一个包含支持度值的字典supportData以备后用。
scanD()函数首先创建一个空字典ssCnt,然后遍历数据集中所有交易记录以及C1中的所有候选集。如果C1中的集合是记录的一部分,那么增加字典中对应的计数值。函数也会先构建一个空列表retList,该列表包含满足最小支持度要求的集合。下一个循环遍历字典中的每个元素并且计算支持度。如果支持度满足最小支持度要求,则将字典元素添加到retList中。函数最后返回最频繁项集的支持度supportData。

程序清单1-2:组织完整的Apriori算法

def aprioriGen(Lk,k):     #创建两个或者三个...元素的候选项集    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 

aprioriGen()的输入参数为频繁项集列表Lk与项集元素个数k,输出为Ck。首先创建一个空列表,然后计算Lk中的元素数目。接下来,比较Lk中的每一个元素与其他元素。紧接着,去列表中的两个集合进行比较。如果这两个集合的前面k-2个元素都相等,那么就将这两个集合合成一个大小为K的集合。这里使用集合的并操作来完成,在python中对应操作符|。

def apriori(dataSet,minSupport = 0.5): #生成所有满足最小支持度的频繁项的集(包含两个或以上元素)    C1 = creatC1(dataSet)    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)        supportData.update(supK)        L.append(Lk)        k += 1    return L,supportData

上面函数的所有操作都被封装在apriori()函数中。给该函数传递一个数据集以及一个支持度,函数会生成候选项集的列表。使用scanD()函数来创建L1,并将L1放入列表L中,L中会包含L1、L2、…。现在有了L1,后面会继续找L2,L3…这可以通过while循环来完成,它创建包含更大项集的更大列表,直到下一个大的项集为空。在循环体中,首先使用aprioriGen()来创建Ck,然后使用scanD()基于Ck来创建Lk。Ck是一个候选项集列表,然后scanD()会遍历Ck,丢掉不满足最小支持度要求的项集。Lk列表被添加到L,同时增加K的值,重复上述过程。最后当Lk为空时,程序返回L并退出。

1.4 从频繁项集中挖掘关联规则
要找到关联规则,我们首先从一个频繁项集开始。从杂货店的例子可以得到,如果有一个频繁项集{豆奶,莴笋},那么就可能有一条关联规则“豆奶—莴笋”。这意味着如果有人购买了豆奶,那么在统计上他会购买莴笋的概率较大。但是,这一条反过来并不总是成立。也就是说,即使“豆奶—莴笋”统计上显著,那么“莴笋—豆奶”也不一定成立。
对于关联规则,我们也有类似的量化方法,这种量化指标称为可信度。为找到感兴趣的规则,我们先生成一个可能的规则列表,然后测试每条规则的可信度。如果可信度不满足最小要求,则去掉该规则。
可以观察到,如果某条规则并不满足最小可信度要求,那么该规则的所有子集也不会满足最小可信度要求。假设规则0,1,2—3并不满足最小可信度要求,那么就知道任何左部为{0,1,2}子集的规则也不会满足最小可信度要求。
可以利用关联规则的上述性质属性来减少需要测试的规则数目。首先从一个频繁项集开始,接着创建一个规则列表,其中规则右部只包含一个元素,然后对这些规则进行测试。接下来合并所有剩余规则来创建一个新的规则列表,其中规则右部包含两个元素。

程序清单1-3 关联规则生成函数

def generateRules(L,supportData,minConf = 0.7):   #生成包含可信度的规则列表    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

generateRules()有3个参数:频繁项集列表、包含那些频繁项集支持度的字典supportData、最小可信度阈值。函数最后生成一个包含可信度的规则列表,这些规则存放在bigRuleList。generateRules()的两个输入参数正好是函数apriori()的输出结果。该函数遍历L中的每一个频繁项集并对每个频繁项集创建只包含单个元素集合的列表H1。因为无法从单元素项集中构建关联规则,所以要从包含两个或者更多元素的项集开始规则构建过程。如果频繁项集的元素数目超过2,那么会考虑对它做进一步的合并,具体合并可以通过函数rulesFromConseq()来完成,后面会详细讨论合并过程。如果项集中只有两个元素,那么使用函数calcConf()来计算可信度值。

def calcConf(freqSet,H,supportData,brl,minConf = 0.7):    #计算可信度值    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

我们的目标是计算规则的可信度以及找到满足最小可信度要求的规则。所有这些可以使用函数calcConf()来完成。

def rulesFromConseq(freqSet,H,supportData,brl,minConf=0.7): #频繁项集中有三个或以上元素时的关联规则怎么划分    m = len(H[0])    if (len(freqSet)>(m+1)):        Hmp1 = aprioriGen(H,m+1)        Hmp1 = calcConf(freqSet,Hmp1,supportData,brl,minConf)        if (len(Hmp1)>1):            rulesFromConseq(freqSet,Hmp1,supportData,brl,minConf)

为从最初的项集中生成更多的关联规则,可以使用rulesFromConseq()函数。该函数有2个参数:一个是频繁项集,另一个是可以出现在规则右部的元素列表H。可以使用函数aprioriGen()来生成H中元素的无重复组合,该结果会存储在Hmp1中,这也是下一次迭代的H列表。Hmp1包含所有可能的规则,可以利用calcConf()来测试它们的可信度以确定规则是否满足要求。如果不止一条规则满足要求,那么使用Hmp1迭代调用函数rulesFromConseq()来判断是否可以进一步组合这些规则。

0 0