zoukankan      html  css  js  c++  java
  • 关联规则算法

    apriori算法

    参考链接:
    https://www.cnblogs.com/pinard/p/6293298.html
    https://www.cnblogs.com/lsqin/p/9342926.html
    https://blog.csdn.net/antkillerfarm/article/details/60880477
    # Apriori算法
    #自底向上,是否可以改进为自顶向下,先拆分,再频数统计
    #首先根据支持度将各元素中低于支持度的子元素删除,再计算?
    """
    由于Apriori算法假定项集中的项是按字典序排序的,而集合本身是无序的,所以我们在必要时需要进行set和list的转换;
    由于要使用字典(support_data)记录项集的支持度,需要用项集作为key,而可变集合无法作为字典的key,因此在合适时机应将项集转为固定集合frozenset。
    支持度
    置信度
    """
    
    import time
    import numpy as np
    from collections import Counter
    
    class apriori_algorithm:
     
        # 算法初始化
        def __init__(self, minSupport, dataSet):
            self.minSupport = minSupport  # 最小支持度
            self.dataSet = dataSet  # 数据集
     
        # 加载数据集
        def loaddata(self):
            return [[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]]
     
        # 生成单个物品的项集列表
        def generateC1(self, dataSet):
            C1 = []  # 用于存放生成的单个物品的项集列表
            # 遍历数据集
            for data in dataSet:
                for item in data:
                    if [item] not in C1:
                        C1.append([item])
            C1.sort()
            #关于map对象的遍历,在内循环中遍历完最后一个元素后,再次访问时会返回空列表,故转为list
            #frozenset集合运算结果均为frozenset
            return list(map(frozenset, C1))
        
        
        def generateL1(self, dataSet, minSupport, support_data):
            # 遍历数据集
            numItems = float(len(dataSet))
            uni_items = {frozenset([k]):v/numItems for k,v in Counter(sum(dataSet, [])).items() if v/numItems >= minSupport}
            L1 = list(uni_items.keys())
            support_data.update(uni_items)
            return L1
        
     
        # 根据候选k项集,找出频繁k项集,并保存支持度信息备用
        def generateLk_by_Ck(self, dataSet, Ck, minSupport, support_data):
            """
               Generate Lk by executing a delete policy from Ck.
               Args:
                   data_set: 数据集
                   Ck: A set which contains all all frequent candidate k-itemsets.
                   min_support: The minimum support.
                   support_data: A dictionary. The key is frequent itemset and the value is support.
               Returns:
                   Lk: A set which contains all all frequent k-itemsets.
               """
            #候选集在样本出现频数统计
            countData = dict()
            for d in map(set, dataSet):  #迭代器,大数据集只访问一次
                for c in Ck:
                    if c.issubset(d):  #子集判断,并非元素判断
                        countData[c] = countData.get(c, 0) + 1
                        
            #由候选集生成频繁项集
            #tmp = {k:v/numItems for k,v in countData.items() if v/numItems >= minSupport}
            #support_data.update(tmp)
            Lk = []  #即Lk
            numItems = float(len(dataSet))
            for key in countData:
                support = countData[key] / numItems
                if support >= minSupport:
                    Lk.append(key)  #Lk.insert(0, key)
                    support_data[key] = support
     
            return Lk
        
        
        #根据频繁k项集产生候选k+1项集
        #通过候选k项集中仅有一个子元素不同的两个元素合成候选k+1项集的元素 -- 频繁项集的子集一定是频繁的
        def generateCK1(self, Lk, k):
            Ck = set()  #防止重复
            len_Lk = len(list(Lk))
            list_Lk = list(Lk)
            for i in range(len_Lk-1):  #len_Lk<=1时,直接返回Ck空
                for j in range(i+1, len_Lk):  #C(n,2)
                    l1 = list(list_Lk[i])
                    l2 = list(list_Lk[j])
                    #l1.sort(); l2.sort()
                    ##还可以判断子集。一定是前两个?任意两个 -- 频繁项集的子集一定是频繁的
                    if l1[0:k - 2] == l2[0:k - 2]:
                        Ck_item = list_Lk[i] | list_Lk[j]
                        if self.isCk(Ck_item, list_Lk):
                            Ck.add(Ck_item)
            return Ck
        
        
        def generateCK(self, Lk, k):
            Ck = set()  #防止重复
            len_Lk = len(Lk)
            for i in range(len_Lk-1):  #len_Lk<=1时,直接返回Ck空
                for j in range(i+1, len_Lk):  #C(n,2)
                    Ck_item = Lk[i] | Lk[j]
                    ##还可以判断子集。一定是前两个?任意两个 -- 频繁项集的子集一定是频繁的
                    if len(Ck_item) == k and max(Lk[i]) not in Lk[j]:
                        if self.isCk(Ck_item, Lk):  #缩减候选集范围
                            Ck.add(Ck_item)
            return Ck
        
        
        # 频繁项集判断
        def isCk(self, Ck_item, list_Lk):
            for item in Ck_item:
                sub_Ck = Ck_item - frozenset([item])
                if sub_Ck not in list_Lk:
                    return False
            return True
        
        
        def generate_L(self, dataSet, k, min_support):
            """
               Generate all frequent itemsets.
               Args:
                   data_set:数据集
                   k: 频繁项集中含有的最多的元素
                   min_support: 最小支持度
               Returns:
                   L: 出现的所有频繁项集
                   support_data: 每个频繁项集对应的支持度
               """
            support_data = {}
            dataSet = dataSet.copy()
            #C1 = self.generateC1(dataSet)
            #L1 = self.generateLk_by_Ck(dataSet, C1, min_support, support_data)
            L1 = self.generateL1(dataSet, min_support, support_data)
            Lksub1 = L1.copy()
     
            L = []
            L.append(Lksub1)
     
            for i in range(2, k + 1):
                Ci = self.generateCK(Lksub1, i)
                if len(Ci) == 0: break  #候选集为空,无法产生频繁集
                Li = self.generateLk_by_Ck(dataSet, Ci, min_support, support_data)
                Lksub1 = Li.copy()
                L.append(Lksub1)
                if len(Li) <= 1: break  #频繁集元素个数小于等于1,无法产生下一轮候选集
            return L, support_data
        
        
        # 生成关联规则
        def generate_big_rules(self, L, support_data, min_conf, min_lift):
            """
            Generate big rules from frequent itemsets.
            Args:
                L: 所有频繁项集的列表
                support_data: 每个频繁项集对应的支持度
                min_conf: 最小可信度
            """
            big_rule_list = []
            if len(L) <= 1:
                print('无法生成关联规则')
                return big_rule_list
            
            sub_set_list = L[0]
            for freq_set in sum(L[1:], []):  #如果数据量较大,还是改为嵌套循环吧
                for sub_set in sub_set_list:
                    if sub_set.issubset(freq_set):
                        conf = support_data[freq_set] / support_data[freq_set - sub_set]
                        lift = conf / support_data[sub_set]
                        big_rule = (freq_set - sub_set, sub_set, conf, lift)
     
                        if conf >= min_conf and lift >= min_lift:  #and big_rule not in big_rule_list
                            print(freq_set - sub_set, " => ", sub_set, "conf: ", conf, "lift: ", lift)
                            big_rule_list.append(big_rule)
                sub_set_list.append(freq_set)
            return big_rule_list
     
    
    if __name__ == '__main__':
        #参考链接:https://www.cnblogs.com/pinard/p/6293298.html
        #https://www.cnblogs.com/lsqin/p/9342926.html
        #https://blog.csdn.net/antkillerfarm/article/details/60880477
        minSup, minConf, minLift = 0.5, 0.5, 1  #提升度lift>1表示正相关, 确信度conviction
        #dataSet = [list(set(i)) for i in np.random.randint(10, size=(10000,10)).tolist()]
        dataSet = [[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5], [1, 3]]  #[[1, 3, 4], [2, 3, 8,9], [1, 2, 3, 5], [2, 8,9]]
        t0 = time.time()
        '''dataSet: list
        10,(10000,10):    2.7s
        100,(1000000,10): 3h+
        '''
        apriori = apriori_algorithm(minSupport=minSup, dataSet=dataSet)
        
        L, support_data = apriori.generate_L(dataSet, 3, minSup)
     
        print('所有频繁项集列表:
    ', L)
        print('所有频繁项集对应的支持度:
    ', support_data)
        big_rule_list = apriori.generate_big_rules(L, support_data, minConf, minLift)
        print('耗时:
    ', time.time() - t0)
        #print('所有满足条件的关联规则:
    ', big_rule_list)
    

      FP-growth算法

    # -*- coding: utf-8 -*-
    """
    Created on Tue Jun 25 15:49:56 2019
    
    @author: epsoft
    """
    
    # FP树类
    import time
    class treeNode:
        def __init__(self, nameValue, numOccur, parentNode):
            self.name = nameValue  #节点元素名称,在构造时初始化为给定值
            self.count = numOccur   # 出现次数,在构造时初始化为给定值
            self.nodeLink = None   # 指向下一个相似节点的指针,默认为None
            self.parent = parentNode   # 指向父节点的指针,在构造时初始化为给定值
            self.children = {}  # 指向子节点的字典,以子节点的元素名称为键,指向子节点的指针为值,初始化为空字典
    
        # 增加节点的出现次数值
        def inc(self, numOccur):
            self.count += numOccur
    
        # 输出节点和子节点的FP树结构
        def disp(self, ind=1):
            print(' ' * ind, self.name, ' ', self.count)
            for child in self.children.values():
                child.disp(ind + 1)
    
    # =======================================================构建FP树==================================================
    
    # 对不是第一个出现的节点,更新头指针块。就是添加到相似元素链表的尾部
    def updateHeader(nodeToTest, targetNode):
        while (nodeToTest.nodeLink != None):
            nodeToTest = nodeToTest.nodeLink
        nodeToTest.nodeLink = targetNode
    
    # 根据一个排序过滤后的频繁项更新FP树
    def updateTree(items, inTree, headerTable, count):
        if items[0] in inTree.children:
            # 有该元素项时计数值+1
            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函数
            updateTree(items[1::], inTree.children[items[0]], headerTable, count)
    
    # 主程序。创建FP树。dataSet为事务集,为一个字典,键为每个事物,值为该事物出现的次数。minSup为最低支持度
    def createTree(dataSet, minSup=1):
        '''dataSet: {frozenset({'h', 'j'}): 1,}'''
        # 第一次遍历数据集,创建头指针表
        headerTable = {}
        for trans in dataSet:
            for item in trans:
                headerTable[item] = headerTable.get(item, 0) + dataSet[trans]
        # 移除不满足最小支持度的元素项
        keys = list(headerTable.keys()) # 因为字典要求在迭代中不能修改,所以转化为列表
        for k in 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) # 根节点
        # 第二次遍历数据集,创建FP树
        for tranSet, count in dataSet.items():
            localD = {} # 记录频繁1项集的全局频率,用于排序
            for item in tranSet:
                if item in freqItemSet:   # 只考虑频繁项
                    localD[item] = headerTable[item][0] # 注意这个[0],因为之前加过一个数据项
            if len(localD) > 0:
                orderedItems = [v[0] for v in sorted(localD.items(), key=lambda x: x[1], reverse=True)] # 排序
                updateTree(orderedItems, retTree, headerTable, count) # 更新FP树
        return retTree, headerTable
    
    # =================================================查找元素条件模式基===============================================
    
    # 直接修改prefixPath的值,将当前节点leafNode添加到prefixPath的末尾,然后递归添加其父节点。
    # prefixPath就是一条从treeNode(包括treeNode)到根节点(不包括根节点)的路径
    def ascendTree(leafNode, prefixPath):
        if leafNode.parent != None:
            prefixPath.append(leafNode.name)
            ascendTree(leafNode.parent, prefixPath)
    
    # 为给定元素项生成一个条件模式基(前缀路径)。basePet表示输入的频繁项,treeNode为当前FP树中对应的第一个节点
    # 函数返回值即为条件模式基condPats,用一个字典表示,键为前缀路径,值为计数值。
    def findPrefixPath(basePat, treeNode):
        condPats = {}  # 存储条件模式基
        while treeNode != None:
            prefixPath = []  # 用于存储前缀路径
            ascendTree(treeNode, prefixPath)  # 生成前缀路径
            if len(prefixPath) > 1:
                condPats[frozenset(prefixPath[1:])] = treeNode.count  # 出现的数量就是当前叶子节点的数量
            treeNode = treeNode.nodeLink  # 遍历下一个相同元素
        return condPats
    
    # =================================================递归查找频繁项集===============================================
    # 根据事务集获取FP树和频繁项。
    # 遍历频繁项,生成每个频繁项的条件FP树和条件FP树的频繁项
    # 这样每个频繁项与他条件FP树的频繁项都构成了频繁项集
    
    # inTree和headerTable是由createTree()函数生成的事务集的FP树。
    # minSup表示最小支持度。
    # preFix请传入一个空集合(set([])),将在函数中用于保存当前前缀。
    # freqItemList请传入一个空列表([]),将用来储存生成的频繁项集。
    def mineTree(inTree, headerTable, minSup, preFix, freqItemList):
        # 对频繁项按出现的数量进行排序进行排序
        sorted_headerTable = sorted(headerTable.items(), key=lambda p: p[1][0])  #返回重新排序的列表。每个元素是一个元组,[(key,[num,treeNode],())
        bigL = [v[0] for v in sorted_headerTable]  # 获取频繁项
        for basePat in bigL:
            newFreqSet = preFix.copy()  # 新的频繁项集
            newFreqSet.add(basePat)     # 当前前缀添加一个新元素
            freqItemList.append(newFreqSet)  # 所有的频繁项集列表
            condPattBases = findPrefixPath(basePat, headerTable[basePat][1])  # 获取条件模式基。就是basePat元素的所有前缀路径。它像一个新的事务集
            myCondTree, myHead = createTree(condPattBases, minSup)  # 创建条件FP树
    
            if myHead != None:
                #print('conditional tree for:', newFreqSet)  #用于测试
                #myCondTree.disp()
                mineTree(myCondTree, myHead, minSup, newFreqSet, freqItemList)  # 递归直到不再有元素
    
    # 生成数据集
    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)] = retDict.get(frozenset(trans), 0) + 1
        return retDict
    
    if __name__=='__main__':
        minSupRate = 0.01
        t0 = time.time()
        #加载数据集
        simpDat = loadSimpDat()
        #simpDat = transactions
        minSup = minSupRate*len(simpDat)
        #转化为符合格式的事务集
        initSet = createInitSet(simpDat)
        #形成FP树
        myFPtree, myHeaderTab = createTree(initSet, minSup)
        #myFPtree.disp()  # 打印树
        
        #生成频繁项集
        freqItems = []
        mineTree(myFPtree, myHeaderTab, minSup, set([]), freqItems)
        print('频繁项集:
    ', freqItems)
        print('耗时:', time.time()-t0)
        
    

      

  • 相关阅读:
    判断一棵二叉树是否为二叉搜索树
    分离链接法的删除操作函数
    线性探测法的查找函数
    Bzoj1251 序列终结者
    POJ2396 Budget
    Bzoj3531: [Sdoi2014]旅行
    Codeforces Round #389 Div.2 E. Santa Claus and Tangerines
    Codeforces Round #389 Div.2 D. Santa Claus and a Palindrome
    Codeforces Round #389 Div.2 C. Santa Claus and Robot
    Codeforces Round #389 Div.2 B. Santa Claus and Keyboard Check
  • 原文地址:https://www.cnblogs.com/iupoint/p/11230472.html
Copyright © 2011-2022 走看看