zoukankan      html  css  js  c++  java
  • 机器学习:Apriori

    优点:易编码实现
    缺点:在大数据集上可能较慢
      
    从大规模数据集中寻找物品间的隐含关系被称作 关联分析(association analysis)或者 关联规则学习(association rule learning)
    比如购买商品 A 的顾客有多大概率同时购买商品 B,比如用户在搜索框输入 "python" 时有多大概率是为了搜索 "python 教程"
      
    项集(item sets)
    经常出现在一块的物品的集合,比如一个顾客购买了 {葡萄酒、尿布、豆奶、莴苣},那么可以说 {葡萄酒,尿布, 豆奶} 或 {葡萄酒、尿布} 等组合是一个项集
      
    项集支持度(support)
    项集在数据集中所占的比例

    频繁项集(frequent item sets)
    超过一定的支持度的项集就是频繁项集
      
    关联规则(association rules)
    暗示两种物品之间可能存在很强的关系,比如购买尿布的人经常会同时购买葡萄酒,那么可以说存在 {尿布}->{葡萄酒} 的关联规则

    关联规则可信度(confidence)
    是针对一条诸如 {尿布}->{葡萄酒} 的关联规则来定义的,这条规则的可信度被定义为:支持度({尿布、葡萄酒})/支持度({尿布})
      
    Apriori 原理
      如果某个项集是频繁的,那么它的所有子集也是频繁的
      反过来说如果一个项集是非频繁集,那么它的所有超集也是非频繁的
      先把所有记录的每个项都取出来,得到集合 C1,集合的每个元素是只有单个项的项集
      取 C1 中出现次数达到阀值的项集记为 L1,L1 的每个元素是只有单个项的频繁项集
      基于 L1 取 C2,C2 每个元素是有两个项的项集,且该项集是 L1 的某个项集的超集
      取 C2 中出现次数达到阀值的项集记为 L2,L2 的每个元素是有两个项的频繁项集
      重复该过程直到无法构造新的 Ck、Lk
      基于频繁项集 L 计算各个关联性的概率,达到要求的既为关联规则
      
    代码:寻找频繁项集

    # coding=utf-8
    
    
    def createC1(dataSet):
        """
        初始化 C1 集合,C1 每个元素是只有一个项的项集
        每条数据是一个数组类似 [葡萄酒,尿布, 豆奶],为方便可以用数字代替变成像 [1,2,3] 这样
        """
    
        C1 = []
        for transaction in dataSet:
            # 遍历每条数据里的每一项
            for item in transaction:
                # 不要重复的
                if not [item] in C1:
                    # 每个项集用一个数组表示,长度为 1,后面会根据这个 C1 构建 C2、C3、Cn
                    C1.append([item])
        C1.sort()
    
        # frozenset 使得项集不可改变
        return map(frozenset, C1)
    
    
    def scanD(D, Ck, minSupport):
        """
        D - 每个元素是一条原始记录(会去掉重复的)
        Ck - 每个元素是有 k 个项的项集
        minSupport - 频繁项集的最小支持度
    
        基于 D, Ck, minSupport 寻找频繁项集 Lk
        """
        ssCnt = {}
        # 遍历每一条原始记录
        for tid in D:
            # 遍历每一个项集
            for can in Ck:
                # 如果该原始记录包含该项集,统计该项集出现的次数
                if can.issubset(tid):
                    if can not in ssCnt:
                        ssCnt[can] = 1
                    else:
                        ssCnt[can] += 1
    
        numItems = float(len(D))
        retList = []
        supportData = {}
    
        # 遍历每一个项集
        for key in ssCnt:
            # 支持度 = 项集出现次数/原始记录总数
            support = ssCnt[key] / numItems
            if support >= minSupport:
                # 支持度大于 minSupport 的是频繁项集
                retList.insert(0, key)
            # 记录所有项集的支持度
            supportData[key] = support
    
        # 返回频繁以及所有项集的支持度
        return retList, supportData
    
    
    def aprioriGen(L, k):
        """
        L - 频繁项集 L(k-1)
    
        通过频繁项集 L(k-1) 计算项集 C(k)
        """
    
        # 用于存 Ck,而 Ck 的每个元素是有 k 个项的项集
        retList = []
        length = len(L)
        for i in range(length):
            for j in range(i + 1, length):
                # list(L[i])[:k-2] 是项集 i 除最后一个数据外的所有数据,比如项集 [1,2,3] 取到的就是 [1,2]
                L1 = list(L[i])[:k - 2]
                L2 = list(L[j])[:k - 2]
                L1.sort()
                L2.sort()
                if L1 == L2:
                    # 比如 [1,2,3] 和 [1,2,4] 取到的 L1 和 L2 就是相等的,并且会合并成 [1,2,3,4]
                    # 这里的原理是这样
                    # 如果 [1,2,3,4] 是频繁项集,那它的子集也必然是频繁项集
                    # 也就是说必然存在频繁项集 [1,2,3] 和 [1,2,4]
                    # 按照这种方法就可以找出所有 L(k) 的潜在组合,也就是 C(k),而不用对所有的组合进行计算
                    # 看该章节的图会更清晰
                    retList.append(L[i] | L[j])
    
        # 返回 Ck
        return retList
    
    
    def apriori(dataSet, minSupport):
        """
        寻找 dataSet 中支持度大于 minSupport 的频繁项集
        """
    
        # 初始化 C1
        C1 = createC1(dataSet)
    
        # 原始记录去掉重复值
        D = map(set, dataSet)
    
        # 取 C1 中支持度大于 minSupport 的频繁项集 L1,以及 C1 中所有项集的支持度
        L1, supportData = scanD(D, C1, minSupport)
    
        # 添加频繁项集 L1
        L = [L1]
    
        k = 2
        # L[k - 2] 是频繁项集 L(k-1)
        while len(L[k - 2]) > 0:
            # 通过频繁项集 L(k-1) 计算 Ck
            # 注意:这里 L[k-2] 其实是数组 L 的最后一个值,该值是频繁项集 L(k-1)
            Ck = aprioriGen(L[k - 2], k)
    
            # 取 Ck 中支持度大于 minSupport 的项集 Lk,以及 Ck 中所有项集的支持度
            Lk, supK = scanD(D, Ck, minSupport)
    
            # ====== 字典的 update:
            # ====== 如果 supK 的 key 在 supportData 也存在则用 supK 值替换 supportData 中的值,
            # ====== 不存在则添加到 supportData
            supportData.update(supK)
    
            # 添加频繁项集 Lk,Lk 的每个元素是一个项集,每个项集有 k 个元素
            L.append(Lk)
    
            # 继续寻找下一个频繁项集 L(k+1)
            k += 1
    
        # 返回所有的频繁项集 L,和所有项集的支持度
        return L, supportData
    

    代码:寻找关联规则

    def calcConf(freqSet, H, supportData, brl, minConf=0.7):
        """
        freqSet - 频繁项集
        H - 每个元素是 freqSet 的子集比如 {1,2},每个元素具有相同长度
        supportData - 每个项集的支持度
        brl - 已找到的关联规则
        minConf - 最小可信度
    
        把 H 的每个元素作为推导结果,freqSet 减去推导结果作为前提,计算可信度
        """
        prunedH = []
        for conseq in H:
            # 关联规则 {freqSet-conseq} -> {conseq} 的可信度 = 支持度(freqSet)/支持度(freqSet-conseq)
            # 既出现 {freqSet-conseq} 时会出现 {conseq} 的概率,可信度最大是 1
            conf = supportData[freqSet] / supportData[freqSet - conseq]
    
            # 只取可信度 > minConf 的规则
            if conf >= minConf:
                # 添加关联规则(前提、推导、可信度)
                brl.append((freqSet - conseq, conseq, conf))
    
                # 添加关联规则的推导
                prunedH.append(conseq)
    
        return prunedH
    
    
    def rulesFromConseq(freqSet, H, supportData, brl, minConf=0.7):
        """
        freqSet - 是频繁项集
        H - 每个元素是频繁项集的子集比如 {1,2},每个元素具有相同长度,每个元素都可作为推导结果
        brl - 存储关联规则
        supportData - 存储所有项集的支持度
        minConf - 最小可信度
        """
    
        # 取 H 每个元素的长度 m,在 generateRules 开始调用时是 1
        m = len(H[0])
    
        # 能进入这个函数的 len(freqSet) 至少是 3,在 generateRules 开始调用时必然大于 m+1,意味着可以合并推导结果
        if len(freqSet) > (m + 1):
            # 比如 freqSet 是 {1,2,3,4}
            # 一开始 H 是 [{1},{2},{3},{4}]
            # 则可以将 H 进一步合并为 [{1,2},{1,3},{1,4},{2,3},{2,4},{3,4}] 产生类似 {3,4}->{1,2} 的结果
    
            # 产生长度为 m+1 的 H
            # 比如产生 [{1,2},{1,3},{1,4},{2,3},{2,4},{3,4}]
            Hmp1 = aprioriGen(H, m + 1)
    
            # 产生推导结果长度为 m+1 的关联规则
            # 比如产生 {3,4}->{1,2}
            Hmp1 = calcConf(freqSet, Hmp1, supportData, brl, minConf)
    
            # 长度为 m+1 的推导结果有多个,可以继续尝试合并为 m+2
            # 比如进一步推导出 {3} -> {1,2,4}
            if len(Hmp1) > 1:
                # 迭代
                rulesFromConseq(freqSet, Hmp1, supportData, brl, minConf)
    
    
    def generateRules(L, supportData, minConf=0.7):
        """
        寻找关联规则
    
        如果某条规则并不满足最小可信度要求,那么该规则的所有子集也不会满足最小可信度要求
        比如 0,1,2->3 不满足最小可信度要求,那么任何左部为 {0,1,2} 子集的规则也不会满足最小可信度要求
    
        L - 频繁项集
        supportData - 项集支持度
        minConf - 最小可信度
    
        L 和 supportData 是 apriori 的返回值
        """
        bigRuleList = []
    
        # 从 L[1] (即 L2) 开始,因为至少有 2 个元素才会有关联
        for i in range(1, len(L)):
            # 遍历 Li 的每个频繁项集
            for freqSet in L[i]:
                # H1 获取频繁项集的每个元素
                H1 = [frozenset([item]) for item in freqSet]
    
                if i > 1:
                    # 项集大于两个元素,可以有多种推导,比如 {0}->{1,2},{0,2}->{1}
                    rulesFromConseq(freqSet, H1, supportData, bigRuleList, minConf)
                else:
                    # 项集只有两个元素,只有一种推导
                    calcConf(freqSet, H1, supportData, bigRuleList, minConf)
    
        # 返回所有可信度大于 minConf 的关联规则
        return bigRuleList
    
    


  • 相关阅读:
    up6-chrome 45+安装教程
    HttpUploader7-授权码配置
    jsp-MySQL连接池
    WordPaster2-正式包布署说明
    HTTP文件上传插件开发文档-ASP
    HTTP文件上传插件开发文档-JSP
    eWebEditor9.x整合教程-Xproer.WordPaster
    42. Trapping Rain Water
    41. First Missing Positive
    40. Combination Sum II
  • 原文地址:https://www.cnblogs.com/moonlight-lin/p/12483685.html
Copyright © 2011-2022 走看看