使用Apriori算法进行关联分析

1、使用Apriori算法进行关联分析

关联分析中最有名的例子是“尿布与啤酒”。据报道,美国中西部的一家连锁店发现,男人们会在周四购买尿布和啤酒。这样商店实际上可以将尿布与啤酒放在一块,并确保在周四全价销售从而获利。当然,这家商店并没有这么做。这是商家通过研究用户的清单来提高销量的一个典型的例子。这种从大规模数据中寻找物品间的隐含关系式十分耗时和复杂的,所需的计算代价也很高。因此需要更加智能的方法在合理的时间范围内找到频繁项集。Apriori算法正是基于该原理得到的。

2、关联分析的一些概念

关联分析是一种在大规模数据集中寻找有趣关系的任务。这些关系可以有两种形式:频繁项集或者关联规则。

这里写图片描述

频繁项集(frequent item sets)是指那些经常出现在一起的物品集合,上图中的集合{葡萄酒,尿布,豆奶}就是频繁项集的一个例子。

关联规则(association rules )暗示两种物品之间可能存在很强的关系。从上图中也可以找到诸如尿布―葡萄酒的关联规则。这意味着如果有人买了尿布,那么他很可能也会买葡萄酒。

有了频繁项集和关联规则,那应该怎么度量他们呢?

一个项集的支持度(support)被定义为数据集中包含该项集的记录所占的比例。从上图中可以得到,{豆奶}的支持度为4/5。而在5条交易记录中有3条包含{豆奶,尿布} , 因此{豆奶,尿布}的支持度为3/5。支持度是针对项集来说的,因此可以定义一个最小支持度,而只保留满足最小支持度的项集。

3、Apriori原理

Apriori算法原理:如果要发现强规则,就必须找到频繁集。

一家商品种类并不多的杂货店,我们对那些经常在一起被购买的商品非常感兴趣。我们只有4种商品:商品0,商品1,商品2和商品3。那么所有可能被一起购买的商品组合都有哪些?

有以下图所示的组合可能

这里写图片描述

图中,0,1,2,3分别表示商品0,商品1,商品2和商品3;空集表示不包含任何物品;物品集合之间的连线表明两个或者更多集合可以组合形成一个更大的集合。

由第二节可知,我们使用频率来度量集合的支持度,如计算{0,3}的支持度,遍历所有的记录找出包含0和3的记录的个数,然后使用该数除以总的记录数,就是所求集合的支持度。

上图可以看出,即使只有4种物品,有15种集合的可能,假如杂货店要买更多的物品(N),那么所有的物品的数据集有2^N-1种项集组合,这将是非常耗时的计算。

为了降低计算机时间,研究人员发现一种所谓的Apriori原理。也就是本小节的第一句话。也就是说,如果我们要减掉那些不是让我们太感兴趣的项集,我们可以将那句话反过来看就行了,如果一个项集是非频繁集,那么它的所有超集也是非频繁的。

如下图所示,已知阴影项集{2,3}是非频繁的。利用这个知识,我们就知道项集{0,2,3} ,{1,2,3丨以及丨0,1,2,3}也是非频繁的。这也就是说,一旦计算出了0,3丨的支持度,知道它是非频繁的之后,就不需要再计算{0,2,3丨、{1,2,3}和 {0,1,2,3}的支持度,因为我们知道这些集合不会满足我们的要求。使用该原理就可以避免项集数目的指数增长,从而在合理时间内计算出频繁项集。

这里写图片描述

4、python实现

# 环境:win7 64位 python3.5
import numpy as np

4.1、使 用 Apriori算法来发现频繁集

该算法首先会生成所有单个物品的项集列表。接着扫描交易记录来查看哪些项集满足最小支持度要求,那些不满足最小支持度的集合会被去掉。然 后 ,对剩下来的集合进行组合以生成包含两个元素的项集。接下来,再重新扫描交易记录,去掉不满足最小支持度的项集。该过程重复进行直到所有项集都被去掉。

伪代码大致如下:

对数据集中的每条交易记录tran
对每个候选项集can1:
检 查 一下can初 是 否 是tran 的 子 集 :
如果是,则增加can的计数值
对每个候遙项集:
如果其支持度不低于最小值,则保留该项集
返回所有频繁项集列表

# 加载数据
def loadDataSet():
    return [[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]]

"""
input:数据集
out:返回不重复的不可修改的含义单个元素的列表
"""
def createC1(dataSet):
    C1 = []
    for transaction in dataSet:
        for item in transaction:
            if not [item] in C1:
                C1.append([item]) 
    C1.sort()
    # frozenset是指“冰冻”的集合,用户是无法修改的
    return list(map(frozenset, C1))   # python3.x是需要加list,python不需要
"""
input:数据集、包含候选集合的列表、最小支持度 
output: retList 返回大于支持度的频繁数据集
        supportData 返回所有元素的支持度
"""
def scanD(D, Ck, minSupport):
    ssCnt = {}
    for tid in D:
        for can in Ck:
            if can.issubset(tid):
                if (can not in ssCnt.keys()): ssCnt[can] = 1  # py3 与 py2 判断字典是否含有某键值不一样
                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

"""
整个Apriori算法的伪代码如下:
当集合中项的个数大于0时
    构建一个k个项组成的候选项集的列表
    检查数据以确认每个项集都是频繁的
    保留频繁项集并构建k+1项组成的候选项集的列表
"""

"""input:频繁项集、项集的个数
        比如:函数以{0}、{1}、{2}作为输入,会生成{0,1}、{0,2}以及{1,2}
   output: 以k个元素组成的集合     
"""
def aprioriGen(Lk, k): #creates Ck
    retList = []
    lenLk = len(Lk)  # 计算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]) # python中| 表示两个集合的并集
    return retList

def apriori(dataSet, minSupport = 0.5):
    C1 = createC1(dataSet)
    D = list(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)  # 返回含有k个元素的项集
        # 返回的Lk是已经满足最小支持度的项集   supK是Ck所有项集的支持度
        Lk, supK = scanD(D, Ck, minSupport)#scan DB to get Lk
        # 追加ck项集的支持度
        supportData.update(supK)
        # 追加满足支持度的项集
        L.append(Lk)
        k += 1
    return L, supportData
dataSet = loadDataSet()
dataSet
[[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]]
C1 = createC1(dataSet)
C1
[frozenset({1}),
 frozenset({2}),
 frozenset({3}),
 frozenset({4}),
 frozenset({5})]
D = list(map(set,dataSet))
D
[{1, 3, 4}, {2, 3, 5}, {1, 2, 3, 5}, {2, 5}]
L1,suppData0 = scanD(D,C1,0.5)
L1
[frozenset({1}), frozenset({3}), frozenset({2}), frozenset({5})]
L,suppDat = apriori(dataSet)
L
[[frozenset({1}), frozenset({3}), frozenset({2}), frozenset({5})],
 [frozenset({3, 5}), frozenset({1, 3}), frozenset({2, 5}), frozenset({2, 3})],
 [frozenset({2, 3, 5})],
 []]
suppDat
{frozenset({5}): 0.75,
 frozenset({3}): 0.75,
 frozenset({2, 3, 5}): 0.5,
 frozenset({1, 2}): 0.25,
 frozenset({1, 5}): 0.25,
 frozenset({3, 5}): 0.5,
 frozenset({4}): 0.25,
 frozenset({2, 3}): 0.5,
 frozenset({2, 5}): 0.75,
 frozenset({1}): 0.5,
 frozenset({1, 3}): 0.5,
 frozenset({2}): 0.75}

4.2、 从频繁项集中挖掘关联规则

上一节介绍如何使用Apriori算法来发现频繁项集,现在需要解决的问题是如何找出关联规则。
从杂货店的例子可以得到,如果有一个频繁项集{豆奶,萬 苣 },那么就可能有一条关联规则 “豆 奶 — 莴苣”。这意味着如果有人购买了豆奶,那么在统计上他会购买莴苣的概率较大。但是,这一条反过来并不总是成立。也就是说,即 使 “豆 奶 — 莴苣”统计上显著,那 么 “萬 苣 —豆奶”也不一定成立。(从逻辑研究上来讲,箭头左边的集合称作前件,箭头右边的集合称为后件。)

对于关联规则,我们也有类似的量化方法 ,这种量化指标称为可信度。一条规则卩— H 的可信度定义为Support(P |H)/support(P)。在python中,操作符丨表示集合的并操作,而数学上集合并的符号是U。P | H是指所有出现在集合P或者集合H 中的元素。

下图是从项集{0,1,2,3}产生的所有关联规则。为找到感兴趣的规则,我们先生成一个可能的规则列表,然后测试每条规则的可信度。如果可信度不满足最小要求,则去掉该规则。

这里写图片描述

可以观察到,如果某条规则并不满足最小可信度要求,那么该规则的所有子集也不会满足最小可信度要求。以图11-4为例,假设规则0,1,2 — 3并不满足最小可信度要求,那么就知道任何左部为{0,1,2}子集的规则也不会满足最小可信度要求。

"""
input: 频繁项集列表、包含频繁项集支持数据的字典、最小可信度阈值
        输入的两个参数正好是apripri()的输出结果
output:
"""
def generateRules(L,supportData,minConf=0.7):
    bigRuleList = []
    for i in range(1,len(L)): # 只获取有两个或更多元素的集合
        for freqSet in L[i]:
            # 对L中的每一个频繁集进行遍历只创建包含单个元素的列表H1
            H1 = [frozenset([item]) for item in freqSet]
            # 如果频繁项集的数目超过2个,考虑对它进一步的合并
            if(i > 1):
                rulesFromConseq(freqSet,H1,supportData,bigRuleList,minConf)
            # 如果项集中只有两个,那么调用calcConf()来计算可信度值
            else:
                calcConf(freqSet,H1,supportData,bigRuleList,minConf)
    return bigRuleList

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

"""
从最初的项集到生成更多的规则
inport: 频繁项集 
"""
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)

def pntRules(ruleList,itemMeaning):
    for ruleTup in ruleList:
        print("")
        for item in ruleTup[0]:
            print(itemMeaning[item])
        print("            ---------->")
        for item in ruleTup[1]:
            print(itemMeaning[item])
        print("confidence: %f " % ruleTup[2])
        print()
L,suppData = apriori(dataSet,minSupport=0.5)
print(L)
print("=======================")
print(suppDat)
print("==========================================")
rules = generateRules(L,suppData,minConf=0.7)
rules
[[frozenset({1}), frozenset({3}), frozenset({2}), frozenset({5})], [frozenset({3, 5}), frozenset({1, 3}), frozenset({2, 5}), frozenset({2, 3})], [frozenset({2, 3, 5})], []]
=======================
{frozenset({5}): 0.75, frozenset({3}): 0.75, frozenset({2, 3, 5}): 0.5, frozenset({1, 2}): 0.25, frozenset({1, 5}): 0.25, frozenset({3, 5}): 0.5, frozenset({4}): 0.25, frozenset({2, 3}): 0.5, frozenset({2, 5}): 0.75, frozenset({1}): 0.5, frozenset({1, 3}): 0.5, frozenset({2}): 0.75}
==========================================
frozenset({1}) ----> frozenset({3}) conf:  1.0
frozenset({5}) ----> frozenset({2}) conf:  1.0
frozenset({2}) ----> frozenset({5}) conf:  1.0





[(frozenset({1}), frozenset({3}), 1.0),
 (frozenset({5}), frozenset({2}), 1.0),
 (frozenset({2}), frozenset({5}), 1.0)]
rules = generateRules(L,suppData,minConf=0.5)
rules
frozenset({5}) ----> frozenset({3}) conf:  0.6666666666666666
frozenset({3}) ----> frozenset({5}) conf:  0.6666666666666666
frozenset({3}) ----> frozenset({1}) conf:  0.6666666666666666
frozenset({1}) ----> frozenset({3}) conf:  1.0
frozenset({5}) ----> frozenset({2}) conf:  1.0
frozenset({2}) ----> frozenset({5}) conf:  1.0
frozenset({3}) ----> frozenset({2}) conf:  0.6666666666666666
frozenset({2}) ----> frozenset({3}) conf:  0.6666666666666666
frozenset({5}) ----> frozenset({2, 3}) conf:  0.6666666666666666
frozenset({3}) ----> frozenset({2, 5}) conf:  0.6666666666666666
frozenset({2}) ----> frozenset({3, 5}) conf:  0.6666666666666666


[(frozenset({5}), frozenset({3}), 0.6666666666666666),
 (frozenset({3}), frozenset({5}), 0.6666666666666666),
 (frozenset({3}), frozenset({1}), 0.6666666666666666),
 (frozenset({1}), frozenset({3}), 1.0),
 (frozenset({5}), frozenset({2}), 1.0),
 (frozenset({2}), frozenset({5}), 1.0),
 (frozenset({3}), frozenset({2}), 0.6666666666666666),
 (frozenset({2}), frozenset({3}), 0.6666666666666666),
 (frozenset({5}), frozenset({2, 3}), 0.6666666666666666),
 (frozenset({3}), frozenset({2, 5}), 0.6666666666666666),
 (frozenset({2}), frozenset({3, 5}), 0.6666666666666666)]

图截取来自:http://www.cnblogs.com/qwertWZ/p/4510857.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值