关联分析是数据挖掘中的重要组成部分,旨在挖掘数据中的频繁模式。我们可以通过一个案例数据库挖掘著名案例来大致了解挖掘频繁项集并产生关联规则。
关联分析的基本概念
关联分析:在大规模数据集中寻找有趣的关系
频繁项集:经常出现在一起的物品集合,即包含0个或者多个项的集合
关联规则:暗示两个物品之间可能存在很强的关系,形如A->B的表达式,规则A->B的度量包括支持度和置信度
支持度:数据集中包含该项集的记录所占的比例,是针对项集来说
置信度:出现某些物品时,另外一些物品必定出现的概率,简单点来说就是在一个物品出现的条件下,另外一个物品出现的概率,是针对规则而言
support(A⇒B)=support_count(A∪B)/N
confidence(A⇒B)=support_count(A∪B)/support_count(A)
在这里再补充说一下闭频繁项集和极大频繁项集的概念
闭频繁项集:如果不存在真超项集Y,使得Y与X在D中具有相同的支持度计数,则项集X是数据集D中的闭频繁项集
极大频繁项集:如果X是频繁的,并且不存在超项集Y使得X是Y的子集,并且Y在D中是频繁的,那么X就是极大频繁项集
这两个概念理解起来有点困难,可以参考频繁项集,闭频繁项集,极大频繁项集这篇文章
关联分析的两种关系
简单关联关系:简单关联关系可以从经典的购物中进行分析,购买面包的顾客80%都会购买牛奶,由于面包和牛奶是早餐搭配的必需品,二者搭配构成了早餐的组成部分,这就是一种简单的关联关系。算法思想基础为:如果某个项集是频繁的,那么它的所有子集也是频繁的,更常用的是它的逆否命题,即如果一个项集是非频繁的,那么它的所有超集也是非频繁的。
序列关联关系:序列关联规则的核心就是找到事务发展的前后关联性。
关联分析步骤
1:发现频繁项集:即计算所有可能组合数的支持度,找出不少于认为设定的最小支持度的集合
2:发现关联规则:这些规则必须满足最小支持度和最小置信度。
频繁项集的挖掘方法
Apriori算法
Apriori算法使用一种称为逐层搜索的迭代方法,其中k项集用于搜索(k+1)项集。首先,通过扫描数据库,累计每个项的计数,并收集满足最小支持度的项,找出频繁1项集。该项集记为L1,。然后使用L1找出频繁2项集的集合L2,使用L2找出L3,如此下去,直到不能再找出频繁k项集。找出每个Lk需要一次数据库的完整扫描。
Apriori算法使用频繁项集的先验知识,先验知识:频繁项集的所有非空子集也一定是频繁的。例如一个项集为{A,B,C}为频繁的,那么他的子集{A},{B},{C},{A,B},{A,C},{B,C},{A,B,C}都是频繁的。
Apri算法只是会挖掘频繁模式,并不会找出关联规则,关联规则需要在找到频繁项集之后再来进行计算。
Apriori算法的两个输入参数分别是数据集和最小支持度。该算法首先会生成所有单个元素的项集列表。接着扫描数据集来查看哪些项集满足最小支持度要求,那些不满足最小支持度的集合会被去掉。然后,对剩下来的集合进行组合以生成包含两个元素的项集。接下来,再重新扫描交易记录,去掉不满足最小支持度的项集。该过程重复进行直到所有项集都被去掉。
该算法需要不断寻找候选集,然后剪枝即去掉非频繁子集的候选集,时间复杂度由暴力枚举所有子集的指数级别O(n2)降为多项式级别,多项式具体系数视底层实现情况而定的。
Ariori算法有两个主要步骤:
1、连接:(将项集进行两两连接形成新的候选集)
利用已经找到的k个项的频繁项集Lk,通过两两连接得出候选集Ck+1,注意进行连接的Lk[i]Lk[j],必须有k−1个属性值相同,然后另外两个不同的分别分布在Lk[i]Lk[i],中,这样的求出的Ck+1为Lk+1的候选集。
2、剪枝:(去掉非频繁项集)
候选集 Ck+1中的并不都是频繁项集,必须剪枝去掉,越早越好以防止所处理的数据无效项越来越多。只有当子集都是频繁集的候选集才是频繁集,这是剪枝的依据。
下图是《数据挖掘概念与分析》中Apriori算法的过程图
算法的实现:
from numpy import *
def loadDataSet():
return [[1,3,4],[2,3,5],[1,2,3,5],[2,5]]
#获取候选1项集,dataset为事务集,返回一个list,每个元素都是set集合
def createC1(dataset):
C1=[] #元素个数为1的项集(非频繁项集,因为还没有同最小支持度比较)
for transaction in dataset:
for item in transaction:
if [item] not in C1:
C1.append([item])
C1.sort() #这里排序是为了生成新的候选集时可以直接认为两个n项候选集前面的部分相同,
#因为除了候选1项集外其他的候选n项集都是以二维列表的形式存在,所以要将候选1项集的每一个元素都转换为一个单独的集合
return list(map(frozenset,C1)) #map(frozenset, C1)的语义是将C1由Python列表转换为不变集合(frozenset,Python中的数据结构)
#找出候选集中的频繁项集
#dataSet为全部数据集,CK为大小为k(包含k个元素)的候选项集,minSupport为设定的最小支持度
def scanD(dataSet,CK,minSupport):
ssCnt={} #记录每个候选项的个数
for tid in dataSet:
for can in CK:
if can.issubset(tid): #判断can是不是tid的子集
ssCnt[can]=ssCnt.get(can,0)+1 #计算每一个项集出现的频率
numItems=float(len(dataSet))
retList=[]
supportData={}
for key in ssCnt:
support=ssCnt[key] / numItems
if support>minSupport:
retList.insert(0,key) #将频繁项集插入返回列表的首部,0表示插入的位置
supportData[key]=support
return retList,supportData #retList为在CK中找出的频繁项集(支持度大于minSupport),supportData记录各频繁集的支持度
#通过频繁项集列表Lk和项集个数k生成项集C(k+1)
def aprioriGen(Lk,k):
retList=[]
lenLK=len(Lk)
for i in range(lenLK):
for j in range(i+1,lenLK): #双重循环遍历除自己以外的项集
#前k-1项相同时,才将两个集合合并,合并后才能生成k+1项
L1=list(Lk[i])[:k-2]
L2=list(Lk[j])[:k-2]
L1.sort()
L2.so