关联分析

来源:互联网 发布:php一句话木马 编辑:程序博客网 时间:2024/06/04 00:38

1、综述

关联分析的目的是寻找交易记录中的频繁项集和关联规则

置信度:set{e1,e2,e3,...}等一起出现的概率

支持度:set{e1,e2,e3...}发生时set{en,em,...}也发生的概率

在频繁项集中寻找关联规则???why?? 置信度和支持度的大小关系??

2、实现思想

a、将item进行排列组合生成各种长度的项集set,满足支持度要求的项集为频繁项集;

b、apriori算法减小了候选项;

c、Fp-Growth算法只需要两次遍历数据库:(1):找出len-1的频繁项集;(2):利用len-1的项集建立FP-Tree

3、apriori实现

# -*- coding:utf8 -*-__author__ = 'zhangpeng'def loadDataSet():    return [[1,3,4],[2,3,5],[1,2,3,5],[2,5]]# 生成len-1的候选项集列表C1def createC1(dataSet):    C1 = []    # 把len-1的不重复item加入C1中    for transaction in dataSet:        for item in transaction:            if not [item] in C1:                C1.append([item])    C1.sort()    # 把c1中每个item变成frozenset集合    return map(frozenset,C1)# 从len-k的候选项集列表Ck中选出频繁项集列表Lkdef scanD(D,Ck,minSupport):    ssCnt = {}    # 遍历数据集和len-k的候选集    for transaction in D:        for item in Ck:            # set集合运算,判断transaction_set中是否包含item_set            if transaction.issuperset(item):                if not ssCnt.has_key(item):                    ssCnt[item] = 1                else:                    ssCnt[item] += 1    numItems = float(len(D))    retList = []    supportData = {}    # 保留大于支持度的item_set    for key in ssCnt:        support = ssCnt[key]/numItems        if support >= minSupport:            retList.append(key)            supportData[key] = support    return retList,supportData# 从len-(k-1)的频繁项集列表Lk_1中生成len-k的候选项集列表Ckdef aprioriGen(Lk_1,k):    retList = []    lenLk_1 = len(Lk_1)    for i in range(lenLk_1):        for j in range(i + 1, lenLk_1):            # 如果两个项集前k-2个元素相同,则合并成len-k的list            temp = frozenset(Lk_1[i] | Lk_1[j])            if len(temp) == k and temp not in retList:                retList.append(temp)    return retList# 寻找dataSet的频繁项集def apriori(dataSet, minSupport = 0.5):    C1 = createC1(dataSet)    D = map(frozenset,dataSet)    L1,supportData = scanD(D,C1,minSupport)    L = []    L.append(L1)    k = 2    while len(L[k-2]) > 0:        Ck = aprioriGen(L[k-2],k)        Lk,supportK = scanD(D,Ck,minSupport)        L.append(Lk)        supportData.update(supportK)        k += 1    return L,supportData# 从频繁项集L中找出关联规则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]   #H1:频繁项集的元素项集列表,作为规则右边的项集列表            if i > 1: #项集长度大于2                rulesFromConseq(freqSet,H1,supportData,bigRuleList,minConf)            else:  #项集长度小于2,直接寻找关联规则                calcConf(freqSet,H1,supportData,bigRuleList,minConf)    return bigRuleList# 对频繁项集进行评估。H:规则右边的项集列表def calcConf(freqSet,H,supportData,brl,minConf = 0.7):    prunedH = []    for item in H:        conf = supportData[freqSet]/supportData[freqSet - item]        if conf >= minConf:            brl.append((freqSet - item,item,conf))            prunedH.append(item)    return prunedH# 生成关联规则候选集,并进行评估def rulesFromConseq(freqSet, H, supportData, brl, minConf = 0.7):    m = len(H[0]) # 规则右边项集的长度    if len(freqSet) > m+1: #项集长度 > 右边项集长度 + 1,这样才能组合出len(m+1)的规则右边项集        Hm = aprioriGen(H,m+1)        Hm = calcConf(freqSet,Hm,supportData,brl,minConf) #利用新的规则右边项集,寻找新的关联规则        if len(Hm) > 1:  #只有规则右边项集 >= 2时,才有可能找到新的频繁项            rulesFromConseq(freqSet,Hm,supportData,brl,minConf)    returnif __name__ == "__main__":    dataSet = loadDataSet()    L,supportData = apriori(dataSet,0.5)    rules = generateRules(L,supportData, 0.7)    # print L    # print supportData    print rules

算法总结:

生成C1候选项集-》挑选L1频繁项集-》生成C2候选项集-》挑选L2频繁项集.......直到length(Lk)=0结束寻找;
从频繁项集freqSet1中寻找后件项集H1,生成候选规则C_ruleH1-》挑选关联规则ruleH1-》利用rukeH1中的H1生成H2,生成候选规则C_ruleH2-》挑选ruleH2........直到Hk的k=len(freqSet1) - 1 or Hk中只有一个元素时停止迭代。接在对频繁项集freqSet2、freqSet3.....重复此过程(非最优解)

代码总结:

频繁项集用set()集合表示,[set1,set2,set3,....]中set集合为list的元素,{frozenset1:value1,frozenset2:value2,....}中frozenset集合为dict的key值,集合可以当做普通的数值类型应用于list和dict的各种方法中。set()集合包含很多集合操作和方法,如-、^、&、|、len()、==、in、is superset()、is subset()、add、update、remove、list(set1)等操作

4、FP-Growth实现

# -*- coding:utf8 -*-class treeNode:    def __init__(self,name,count,parent):        self.name = name        # 节点在某一序列中出现的次数        self.count = count        # 指向此item对应的下一个节点        self.nodeLink = None        self.parent = parent        # 此节点的子节点,{item1:item1Node,item2:item2Node,......}        self.children = {}    def inc(self,count):        self.count += count    def disp(self,ind = 1):        print ' '*ind, self.name, '', self.count        for child in self.children.values():            child.disp(ind + 1)# 创建FpGrowth树def createTree(dataSet,minSup = 1):    headerTable = {}    # 第一次遍历数据库找出len1的频繁项    for trans in dataSet:        trans2set = set(trans)        for item in trans2set:            headerTable[item] = headerTable.get(item,0) + 1    # len1满足条件的item和其出现的次数加入头指针表中    for key in headerTable.keys():        if headerTable[key] < minSup:            del(headerTable[key])    # len1的频繁项集    freqSet1 = set(headerTable.keys())    if len(freqSet1) == 0:        return None,None    for key in headerTable:        # 头指针表value的第二个元素为该item节点链表的起始节点        headerTable[key] = [headerTable[key],None]    # 根节点(父节点)    rootNode = treeNode('Null Set',1,None)    # 第二次遍历数据库,构建FP树    for trans in dataSet:        localD = {}        for item in trans:            if item in freqSet1:                # 获取一条trans中频繁元素和次数                localD[item] = headerTable[item][0]        if len(localD) > 0:            # 将trans中频繁元素按频率从大到小排序            orderedItems = [item[0] for item in sorted(localD.items(),key=lambda line:line[1],reverse=True)]            # 把trans中排好序的元素列表插入树中            updateTree(orderedItems,rootNode,headerTable)    return rootNode,headerTable# 根据父节点(最初为根节点)、排好序的元素列表、头指针表,来更新树和头指针表中的链表def updateTree(items,parentNode,headerTable):    # 父节点含有此item的子节点    if items[0] in parentNode.children.keys():        parentNode.children[items[0]].inc(1)    else:        # 创建节点接入父节点中        parentNode.children[items[0]] = treeNode(items[0],1,parentNode)        # 头指针表没有写入此item的第一个        if headerTable[items[0]][1] == None:            headerTable[items[0]][1] = parentNode.children[items[0]]        else:            #更新头指针表中此item对应节点的链表            updateHeader(headerTable[items[0]][1],parentNode.children[items[0]])    # 传入的项集len >= 2,迭代更新树    if len(items) >= 2:        # 剩下的元素列表在前一个节点后插入        updateTree(items[1:],parentNode.children[items[0]],headerTable)    return# 传入头指针表该item的第一个节点,新的此item的节点,将新的此item节点插入到树中此item节点组成的链表上def updateHeader(nodeToTest,targetNode):    while nodeToTest.nodeLink != None:        nodeToTest = nodeToTest.nodeLink    nodeToTest.nodeLink = targetNodedef loadData():    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# 从leafNode出发上溯寻找节点,找到后将item加入到前缀路径prefixPath中def ascendTree(leafNode,prefixPath):    # 当leafNode是根节点时停止递归    if leafNode.parent != None:        prefixPath.append(leafNode.name)        ascendTree(leafNode.parent,prefixPath)    return# 传入item和其对应的第一个节点,遍历发现该item所有节点的前缀路径,组合成条件模式基def findPrefixPath(item,treeNode):    # item的条件模式基    condPats = []    while treeNode != None:        prefixPath = []        ascendTree(treeNode,prefixPath)        if len(prefixPath) > 1:            # 前缀路径最后一个元素表示此前缀路径出现的次数            # 出现n此,则item的条件基包含n次该前缀路径,即此路径和item共同出现过n次            for i in range(treeNode.count):                condPats.append(prefixPath[1:])        treeNode = treeNode.nodeLink    return condPats# 传入根节点、头指针表、频繁项集的后缀、最终频繁项集的列表# 对头指针表中的每个item搜索条件FP树,用来扩充前缀def mineTree(rootNode,headerTable,minSup,preFix,freqItemList):    # 头指针表中item按从小到大排序    bigL = [v[0] for v in sorted(headerTable.items(),key=lambda p:p[1][0])]    for basePat in bigL:        # 对后缀项集进行浅赋值,新建内容相同的后缀项集,用来加入新元素,并且不改变之前的频繁项中的元素        newFreqSet = preFix.copy()        # 后缀中增加新的item        newFreqSet.add(basePat)        # 频繁项集列表中加入扩充了的前缀        freqItemList.append(newFreqSet)        # 获取item的条件模式基        condPattBases = findPrefixPath(basePat,headerTable[basePat][1])        # 利用item创建“条件FP树”和“新头指针表”,新头指针表中item和前缀同时出现的次数都满足最小支持度        myCondTree,myHead = createTree(condPattBases,minSup)        # 条件树还有元素,还能对前缀进行扩充        if myHead != None:            mineTree(myCondTree,myHead,minSup,newFreqSet,freqItemList)if __name__ == "__main__":    dataSet = loadData()    FpTree,headerTable = createTree(dataSet,3)    # FpTree.disp()    freqSetList = []    mineTree(FpTree,headerTable,3,set({}),freqSetList)    print freqSetList
算法主要步骤:
(1)第一次扫描数据库,找出满足支持度的item,建立item对应的头指针表,包含此item出现的次数(即指向item头节点的指针表);
(2)第二次扫描数据库,对每个trans过滤掉非频繁item,然后按item出现的次数从大到小排序,将排好序的序列中每个元素插入(部分合并,因为共用一些路径)到树中,并更新头指针表中对应的链表,建立FP-Tree;
(3)FP搜索:从原始头指针表中出现次数最少的item-level1开始,把其作为频繁项的前缀,找出其所有的前缀路径构成条件模式基,条件基作为输入构建条件FP-Tree和条件头指针表,条件头指针表中每个item-level2和之前的后缀item-level1共同出现的次数满足支持度,这里面的每个item-level2会和后缀item-level1组成新的频繁项集,然后分别把其中每一个item-level2作为新的后缀,找出其对应的条件模式基,利用新的条件模式构建新的条件FP-Tree和新的条件头指针表,若头指针表非空,则新头指针表里面的每个item-level3会和此其后缀item-level2、之前的后缀item-level1组成新的频繁项集;如为空,则停止递归,然后换一个新后缀item-level2重复重复,直到所有item-level2元素用完......接下来从原始头指针表中出现次数第二少的item-lwvel1出发,直到遍历完里面的所有item-level1,这样最终找到所有的频繁项集。



0 0
原创粉丝点击