机器学习实战--笔记9(Apriori算法)

Apriori算法也属于无监督学习,它强调的是“从数据X中能够发现什么”。从大规模的数据集中寻找物品之间隐含关系被称为关联分析或者称为关联规则学习。这里的主要问题在于,寻找物品的不同组合是一项十分耗时的任务,所需的计算代价很高,蛮力搜索并不能解决这个问题。因此此处介绍使用Apriorio算法来解决上述问题。

1:简单概念描述

(1)              频繁项集:指经常出现在一块的物品的集合。 关联规则暗示两种物品之间存在很强的关系。(这里我们事先定义阀值,超过该阀值,证明两者之间存在很强的关系).

(2)              一个项集的支持度(support)被定义为数据集中包含该项集的记录所占的比例。我们事先需要定义一个最小支持度(minSupport),而只保留满足最小支持度的项集。

(3)              可信度或置信度(confidence)是针对一条诸如{尿布}->{葡萄酒}的关联规则来定义的。

(4)              Apriori的原理是如果某个项集是频繁的,那么它的子集也是频繁的。反过来说,如果一个项集是非频繁的,那么它的所有超集也是非频繁的。比如{1,2}出现的次数已经小于最小支持度了(非频繁的),那么超集{0,1,2}的组合肯定也是非频繁的了。主要包括发现频繁项集和挖掘关联规则这两步。

2:发现频繁项集

过程是:从C1= {{0},{1},{2},{3}}开始,然后生成L1,L1是C1中项集的支持度大于等于最小支持度,比如L1 = {{0},{1},{3}}。然后由L1组合得到C2 = {{01},{03},{13}}。一直进行下去直到Ck为空。

[python] view plain copy
print ?
  1. # 加载数据  
  2. def loadDataSet():  
  3.     return [[1,3,4], [2,3,5], [1,2,3,5], [2,5]]  
  4.   
  5. # 创建C1  
  6. def createC1(dataSet):  
  7.     C1 = []  
  8.     for transaction in dataSet:  
  9.         for item in transaction:  
  10.             if not[item] in C1:  
  11.                 C1.append([item])  
  12.     C1.sort()  
  13.     return map(frozenset, C1)            #frozenset 可以将集合作为字典的键字使用  
  14.   
  15. # 由Ck生成Lk  
  16. def scanD(D, Ck, minSupport):  
  17.     ssCnt = {}  
  18.     for tid in D:  
  19.         for can in Ck:  
  20.             if can.issubset(tid):  
  21.                 if not ssCnt.has_key(can):ssCnt[can] = 1  
  22.                 else: ssCnt[can] += 1  
  23.     numItems = float(len(D))  
  24.     retList = []  
  25.     supportData = {}  
  26.     for key in ssCnt:  
  27.         support = ssCnt[key]/numItems  
  28.         if support >= minSupport:  
  29.             retList.insert(0, key)            #在列表的首部插入任意新的集合  
  30.         supportData[key] = support  
  31.     return retList, supportData  
  32.   
  33. #Apriori 算法  
  34. #  由Lk 产生Ck+1  
  35. def aprioriGen(Lk, k):  
  36.     retList = []  
  37.     lenLk = len(Lk)  
  38.     for i in range(lenLk):  
  39.         for j in range(i+1, lenLk):  
  40.             L1 = list(Lk[i])[:k-2]; L2 = list(Lk[i])[:k-2]  
  41.             L1.sort(); L2.sort()  
  42.             if L1 == L2:  
  43.                 retList.append(Lk[i] | Lk[j])  
  44.     return retList  
  45.   
  46. def apriori(dataSet, minSupport = 0.5):  
  47.     C1 = createC1(dataSet)  
  48.     D = map(set, dataSet)  
  49.     L1, supportData = scanD(D, C1, minSupport)  
  50.     L = [L1]  
  51.     k = 2  
  52.     while(len(L[k-2]) > 0):  
  53.         Ck = aprioriGen(L[k-2], k)  
  54.         Lk, supK = scanD(D,Ck, minSupport)  
  55.         supportData.update(supK)  
  56.         L.append(Lk)  
  57.         k += 1  
  58.     return L, supportData  
# 加载数据
def loadDataSet():
    return [[1,3,4], [2,3,5], [1,2,3,5], [2,5]]





创建C1

def createC1(dataSet):
C1 = []
for transaction in dataSet:
for item in transaction:
if not[item] in C1:
C1.append([item])
C1.sort()
return map(frozenset, C1) #frozenset 可以将集合作为字典的键字使用

由Ck生成Lk

def scanD(D, Ck, minSupport):
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 >= minSupport:
retList.insert(0, key) #在列表的首部插入任意新的集合
supportData[key] = support
return retList, supportData

Apriori 算法

由Lk 产生Ck+1

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[i])[:k-2]
L1.sort(); L2.sort()
if L1 == L2:
retList.append(Lk[i] | Lk[j])
return retList

def apriori(dataSet, minSupport = 0.5):
C1 = createC1(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



注意:(1)C1是大小为1的所有候选项集的集合

(2)这里使用了python的frozenset类型。frozenset是指被“冰冻”的集合,就说它们是不可改变的,即用户不能修改它们。这里必须使用frozenset而不是set类型,因为之后必须要将这些集合作为字典键值使用,使用frozenset可以实现这一点,而set却做不到。

3:从频繁项集中发现关联规则

[python] view plain copy
print ?
  1. #从频繁项集中发现关联规则  
  2. def generateRules(L, supportData, minConf=0.7):  #supportData is a dict coming from scanD  
  3.     bigRuleList = []  
  4.     for i in range(1, len(L)):#only get the sets with two or more items  
  5.         for freqSet in L[i]:  
  6.             H1 = [frozenset([item]) for item in freqSet]  
  7.             if (i > 1):  
  8.                 rulesFromConseq(freqSet, H1, supportData, bigRuleList, minConf)  
  9.             else:  
  10.                 calcConf(freqSet, H1, supportData, bigRuleList, minConf)  
  11.     return bigRuleList           
  12.   
  13. def calcConf(freqSet, H, supportData, brl, minConf=0.7):  
  14.     prunedH = [] #create new list to return  
  15.     for conseq in H:  
  16.         conf = supportData[freqSet]/supportData[freqSet-conseq] #calc confidence  
  17.         if conf >= minConf:   
  18.             print freqSet-conseq,‘–>’,conseq,‘conf:’,conf  
  19.             brl.append((freqSet-conseq, conseq, conf))  
  20.             prunedH.append(conseq)  
  21.     return prunedH  
  22.   
  23. def rulesFromConseq(freqSet, H, supportData, brl, minConf=0.7):  
  24.     m = len(H[0])  
  25.     if (len(freqSet) > (m + 1)): #try further merging  
  26.         Hmp1 = aprioriGen(H, m+1)#create Hm+1 new candidates  
  27.         Hmp1 = calcConf(freqSet, Hmp1, supportData, brl, minConf)  
  28.         if (len(Hmp1) > 1):    #need at least two sets to merge  
  29.             rulesFromConseq(freqSet, Hmp1, supportData, brl, minConf)  
#从频繁项集中发现关联规则
def generateRules(L, supportData, minConf=0.7):  #supportData is a dict coming from scanD
    bigRuleList = []
    for i in range(1, len(L)):#only get the sets with two or more items
        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):
    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)

4:使用FP-growth算法来高效发现频繁项集

每次增加频繁项集的大小,Apriori算法都会重新扫描整个数据集。当数据集很大时,这会显著降低频繁项集发现的速度。而FP-growth树只需要对数据库进行两次遍历,能够显著加快频繁项集的速度。但是该算法不能用于发现关联规则。

第一遍对所有元素项的出现次数进行计数,只用来统计出现的频率。而第二遍扫描只考虑那些频繁元素,用来构建FP树。

[python] view plain copy
print ?
  1. # -*- coding: cp936 -*-  
  2. #创建FP树的数据结构  
  3. class treeNode:  
  4.     def __init__(self, nameValue, numOccur, parentNode):  
  5.         self.name = nameValue  
  6.         self.count = numOccur  
  7.         self.nodeLink = None  
  8.         self.parent = parentNode  
  9.         self.children = {}  
  10.   
  11.     def inc(self, numOccur):  
  12.         self.count += numOccur  
  13.   
  14.     def disp(self, ind = 1):  
  15.         print ‘ ’*ind, self.name, ‘ ’self.count  
  16.         for child in self.children.values():  
  17.             child.disp(ind+1)  
  18.               
  19. # 加载数据  
  20. def loadSimpDat():  
  21.     simpDat = [[’r’‘z’‘h’‘j’‘p’],  
  22.                [’z’‘y’‘x’‘w’‘v’‘u’,‘t’’s’],  
  23.                [’z’],  
  24.                [’r’,‘x’,‘n’,‘o’,’s’],  
  25.                [’y’‘r’,‘x’,‘z’,‘q’,‘t’,‘p’],  
  26.                [’y’,‘z’,‘x’,‘e’,‘q’,’s’,‘t’,‘m’]]  
  27.     return simpDat  
  28.   
  29. def createInitSet(dataSet):  
  30.     retDict = {}  
  31.     for trans in dataSet:  
  32.         retDict[frozenset(trans)] = 1  
  33.     return retDict  
  34.   
  35. #构建FP树  
  36. def createTree(dataSet, minSup = 1):  
  37.     headerTable = {}  
  38.     for trans in dataSet:         #计算每个元素出现的频率  
  39.         for item in trans:  
  40.             headerTable[item] = headerTable.get(item, 0) + dataSet[trans]  
  41.     for k in headerTable.keys():    #移除不满足最小支持度的元素项  
  42.         if headerTable[k] < minSup:  
  43.             del[headerTable[k]]  
  44.     freqItemSet = set(headerTable.keys())  
  45.     if len(freqItemSet) == 0return NoneNone    #如果没有数据项满足要求,则退出  
  46.     for k in headerTable:  
  47.         headerTable[k] = [headerTable[k], None]  
  48.     retTree = treeNode(’Null Set’1None)  
  49.     for tranSet, count in dataSet.items():     #根据全局频率对每个事务中的元素进行排序  
  50.         localD = {}  
  51.         for item in tranSet:  
  52.             if item in freqItemSet:  
  53.                 localD[item] = headerTable[item][0]  
  54.         if len(localD) > 0:  
  55.             orderedItems = [v[0for v in sorted(localD.items(),key = lambda p:p[1], reverse = True)]     #使用排序后的频率项集对树进行填充  
  56.             updateTree(orderedItems, retTree, headerTable, count)  
  57.     return retTree, headerTable  
  58.   
  59. def updateTree(items, inTree, headerTable, count):  
  60.     if items[0in inTree.children:  
  61.         inTree.children[items[0]].inc(count)  
  62.     else:  
  63.         inTree.children[items[0]] = treeNode(items[0], count, inTree)  
  64.         if headerTable[items[0]][1] == None:  
  65.             headerTable[items[0]][1] = inTree.children[items[0]]  
  66.         else:  
  67.             updateHeader(headerTable[items[0]][1], inTree.children[items[0]])  
  68.     if len(items) > 1:  
  69.         updateTree(items[1::], inTree.children[items[0]], headerTable, count)    #对剩下的元素项迭代调用updateTree函数  
  70.   
  71. def updateHeader(nodeToTest, targetNode):  
  72.     while(nodeToTest.nodeLink != None):  
  73.         nodeToTest = nodeToTest.nodeLink  
  74.     nodeToTest.nodeLink = targetNode  
# -*- coding: cp936 -*-




#创建FP树的数据结构 class treeNode: def __init__(self, nameValue, numOccur, parentNode): self.name = nameValue self.count = numOccur self.nodeLink = None self.parent = parentNode self.children = {} def inc(self, numOccur): self.count += numOccur def disp(self, ind = 1): print ' '*ind, self.name, ' ', self.count for child in self.children.values(): child.disp(ind+1) # 加载数据 def loadSimpDat(): simpDat = [['r', 'z', 'h', 'j', 'p'], ['z', 'y', 'x', 'w', 'v', 'u','t', 's'], ['z'], ['r','x','n','o','s'], ['y', 'r','x','z','q','t','p'], ['y','z','x','e','q','s','t','m']] return simpDat def createInitSet(dataSet): retDict = {} for trans in dataSet: retDict[frozenset(trans)] = 1 return retDict #构建FP树 def createTree(dataSet, minSup = 1): headerTable = {} for trans in dataSet: #计算每个元素出现的频率 for item in trans: headerTable[item] = headerTable.get(item, 0) + dataSet[trans] for k in headerTable.keys(): #移除不满足最小支持度的元素项 if headerTable[k] < minSup: del[headerTable[k]] freqItemSet = set(headerTable.keys()) if len(freqItemSet) == 0: return None, None #如果没有数据项满足要求,则退出 for k in headerTable: headerTable[k] = [headerTable[k], None] retTree = treeNode('Null Set', 1, None) for tranSet, count in dataSet.items(): #根据全局频率对每个事务中的元素进行排序 localD = {} for item in tranSet: if item in freqItemSet: localD[item] = headerTable[item][0] if len(localD) > 0: orderedItems = [v[0] for v in sorted(localD.items(),key = lambda p:p[1], reverse = True)] #使用排序后的频率项集对树进行填充 updateTree(orderedItems, retTree, headerTable, count) return retTree, headerTable def updateTree(items, inTree, headerTable, count): if items[0] in inTree.children: inTree.children[items[0]].inc(count) else: inTree.children[items[0]] = treeNode(items[0], count, inTree) if headerTable[items[0]][1] == None: headerTable[items[0]][1] = inTree.children[items[0]] else: updateHeader(headerTable[items[0]][1], inTree.children[items[0]]) if len(items) > 1: updateTree(items[1::], inTree.children[items[0]], headerTable, count) #对剩下的元素项迭代调用updateTree函数 def updateHeader(nodeToTest, targetNode): while(nodeToTest.nodeLink != None): nodeToTest = nodeToTest.nodeLink nodeToTest.nodeLink = targetNode



            </div>
                </div>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值