当前位置:   article > 正文

python实现Apriori算法_python apriori

python apriori

★ 关联分析:

        从大规模数据集中寻找物品间的隐含关系被称作关联分析。而寻找物品的不同组合是一项十分耗时的任务,所需的计算代价很高。Apriori算法正是来解决这一问题。

        物品之间的关系一般可以有两种形式:频繁项集和关联规则。

        频繁项集:数据集中经常出现在一块的物品的集合。

        关联规则:两种物品之间可能存在很强的关系。

     ✿ 下面举一个大家很熟悉的 <<机器学习实战>> 上的例子:

交易代号商品
0豆奶、莴苣
1莴苣、尿布、葡萄酒、甜菜
2豆奶、尿布、葡萄酒、橙汁
3莴苣、豆奶、尿布、葡萄酒
4莴苣、豆奶、尿布、橙汁

        我们观察这5笔交易,发现集合{葡萄酒、尿布、豆奶}就是频繁项集的其中一个例子(当然{豆奶},{莴苣},{豆奶、莴苣}等还有很多集合也是频繁项集),而关联规则也可以找出,如:尿布 --> 葡萄酒,它意味着如果有人买了尿布,那么他很有可能也会买了葡萄酒。

       如何度量这些关系呢?说白了就是项集怎么样才算是频繁?什么样的规则才能让我们信服?我们用支持度可信度来度量这些有趣的关系。

       项集的支持度:数据集中包含该项集的记录的比例。如{豆奶}的支持度为:45

       可信度: 对于关联规则{尿布} --> {葡萄酒},它的可信度定义为:支持度({尿布,葡萄酒}) / 支持度({尿布}),即35/45=34

       ✿ 所谓的Apriori原理:

       如果一个项集是非频繁集,那么它的所有超集也是非频繁的。

       ✿ Apriori原理的理解

        (1.) 项集的支持度可以理解为数学中的概率。而超集就是多个事件的交。

         比如:豆奶的支持度为:45,而在数学的概率论中,我们可以记购买了豆奶为事件A,那么A发生的概率就是:P(A)=45,我们再记购买了尿布为事件B,那么集合{豆奶、尿布}是{豆奶}的超集,它的支持度是:35,而在概率论中,A与B同时发生的概率也是:P(A•B)=35,因此我们这么理解支持度是对的。

        (2.) 由概率公式我们知道:P(A•B) ≤ P(A) ,通俗点讲就是:P(A的超集) ≤ P(A),因此既然支持度就是概率,只要P(A)都不满足最小支持度要求了,那么P(A的超集) 肯定更小,它更不会满足要求,这也是我们的Apriori原理的解释。

        (3.) 可信度就是条件概率。

       因此,当一个项集是非频繁的,我们不需要计算它的超集了,因为它的超集不会满足我们的要求,这样就避免了项集数目的指数增长,从而在合理的时间内计算出频繁项集。

★  Apriori算法之产生频繁项集:

     ✿ 代码理解和疑问:

      1.  产生频繁项集的流程:

        (1.) Ck是候选项集列表,其中它的每个子项的大小都为k,如C1= [ {1},{2},{3}...],C2=[ {1,2},{1,3},{2,3}...]

        (2.) Lk是频繁项集列表,它是由Ck筛选出的满足最小支持度的项的集合。

       首先我们创造出候选项集C1,C1是所有子项大小为1的集合列表,然后筛选出L1,再由L1自由组合创造出C2,然后把C2筛选出L2,这样一直创造筛选,直到创造出的Ck为空,那么停止。

      2. loadDataSet()函数的理解:

       该函数创造一个数据集,模拟商店购买记录,比如此数据集模拟了四次事务,第一次买了1、3、4号商品,第二次买了2、3、5号商品

      3. createC1()函数的疑问

       该函数创造出C1,函数中对C1进行了排序,不明白它的意思。假设它是为了C1变得有序,那么接下来的scanD()函数是对C1筛选出L1,scanD()函数却没有对L1进行排序,根据原程序的意思筛选后的L1的顺序跟原始数据集dataSet有关,比如原始数据集现在是 [[1,3,4], [2,3,5], [1,2,3,5], [2,5]],那么稍微变换每个子项的顺序[[3,1,4], [2,3,5], [1,2,3,5], [2,5]],得到的L1顺序就不一样,那么createC1这里的排序意义在哪儿?请路过的大神解答 。

       4. scanD()函数的理解与疑问:

        该函数作用是从候选项集Ck 筛选出满足最小支持度的子项从而构成频繁项集Lk ,并且保存了每个频繁项的支持度,但是该函数返回Lk时使用了insert()函数,该函数的复杂度是O(n),而append()函数的复杂度是O(1),不知为何作者使用复杂度更高的insert(),难道是与前面的排序相对应?请大神解答。

       ✿ 5. aprioriGen()函数的疑问:

        该函数作用是通过频繁项集Lk1产生候选项集Ck,因为Lk1的每个子项有k-1个元素,而Ck每个子项有k个元素,所以该函数就是把Lk1的两个子项合并起来,形成一个具有k个元素的新项,比如Lk1的某两个项是{0,1}和{0,2},合并后是{0,1,2},这样就组合成了Ck的一个子项。因为合并后的新项不是所有都满足条件,因为我们不要忽视最重要的条件:Ck的每个子项的大小为k,也就是每个子项有k个元素,比如Lk1(此时k为3)的某两个子项是{0,1}和{2,3},他俩合并后的新项就是{0,1,2,3},有4个元素,不满足C3每个子项均有3个元素的要求,因此原程序想了一个办法:比较Lk1的两个项的前k-2个元素,如果相同就合并,不同就不合并,这样做比较容易理解:Lk1的某两个待合并的项有k-1个元素,假如k-2个元素都相同了,那就一个元素不同了,合并之后肯定只有k个元素,这就满足要求了,比如Lk1的某两个项是{0,1}和{0,2},合并后是{0,1,2}。

       我的疑问是:程序中对Lk1的两个待合并子项都进行了排序,因此只比较了前k-2个元素是否相同,这里注意,有个 ”前“ 字,为何只比较”前“k-2个元素就足够了呢?刚才举的{0,1}和{0,2}例子确实合并后只有3个元素,但是例子:{0,1}和{1,2}合并之后 {0,1,2} 也是只有3个元素,不也是满足我们的Ck子项只有k个元素的要求吗?但它们的前k-2个元素却不相同。

        aprioriGen()的作用是创造出所有满足Ck的每个子项均有k个元素的所有组合,而scanD()的作用才是根据支持度筛选出符合最小支持度的频繁项集Lk,所以我对aprioriGen()函数进行了修改,使其产生所有的组合,但是这样每一层产生的候选项集Ck明显就增多了,执行效率应该会慢一些,所以我在考虑,作者用了3个sort()进行排序,又不辞辛苦使用了insert()函数,又对集合转化成列表再切片比较前k-2个元素,这么大费周章的是不是有什么数学原理可以减少Ck的项数,即:不仅scanD()函数是筛选Ck,本身产生候选项集的aprioriGen()函数也在筛选着Ck ?

       我对aprioriGen()的修改是:去掉createC1()的排序  (sort的复杂度是O(n logn))

  1. def aprioriGen(Lk, k): # 新版aproriGen
  2. lenLk = len(Lk)
  3. temp_dict = {} # 临时字典,存储
  4. for i in range(lenLk):
  5. for j in range(i+1, lenLk):
  6. L1 = Lk[i]|Lk[j] # 两两合并,执行了 lenLk!次
  7. if len(L1) == k: # 如果合并后的子项元素有k个,满足要求
  8. if not L1 in temp_dict: # 把符合的新项存到字典的键中,使用字典可以去重复,比如{1,2,3}和{3,1,2}是一样的项,使用了字典就可以达到去重的作用
  9. temp_dict[L1] = 1
  10. return list(temp_dict) # 把字典的键转化为列表

          接下来是完整的创造频繁项集列表的程序:

  1. from numpy import *
  2. def loadDataSet():
  3. return [[1,3,4], [2,3,5], [1,2,3,5], [2,5]]
  4. def createC1(dataSet): # 创造候选项集C1,C1是大小为1的所有候选项集的集合
  5. C1 = []
  6. for transaction in dataSet:
  7. for item in transaction:
  8. if not [item] in C1:
  9. C1.append([item])
  10. # C1.sort() # 从大到小排序
  11. return list(map(frozenset,C1))
  12. def scanD(D,Ck,minSupport): # 此函数计算支持度,筛选满足要求的项集成为频繁项集Lk,D是数据集,Ck为候选项集C1或C2或C3 ...
  13. ssCnt = {}
  14. for tid in D:
  15. for can in Ck:
  16. if can.issubset(tid):
  17. if not can in ssCnt:
  18. ssCnt[can] = 1
  19. else:
  20. ssCnt[can] += 1
  21. numItems = float(len(D))
  22. retList = []
  23. supportData = {}
  24. for key in ssCnt:
  25. support = ssCnt[key]/numItems # 计算支持度
  26. if support >= minSupport: # 如果支持度大于设定的最小支持度
  27. # retList.insert(0,key)
  28. retList.append(key)
  29. supportData[key] = support
  30. return retList, supportData
  31. def aprioriGen(Lk, k): # 新版aproriGen
  32. lenLk = len(Lk)
  33. temp_dict = {} # 临时字典,存储
  34. for i in range(lenLk):
  35. for j in range(i+1, lenLk):
  36. L1 = Lk[i]|Lk[j] # 两两合并,执行了 lenLk!次
  37. if len(L1) == k: # 如果合并后的子项元素有k个,满足要求
  38. if not L1 in temp_dict: # 把符合的新项存到字典的键中,使用字典可以去重复,比如{1,2,3}和{3,1,2}是一样的项,使用了字典就可以达到去重的作用
  39. temp_dict[L1] = 1
  40. return list(temp_dict) # 把字典的键转化为列表
  41. # def aprioriGen(Lk, k): # 根据频繁项集Lk-1创造候选项集Ck
  42. # retList = []
  43. # lenLk = len(Lk)
  44. # for i in range(lenLk):
  45. # for j in range(i+1, lenLk):
  46. # L1 = list(Lk[i])[:k-2]
  47. # L2 = list(Lk[j])[:k-2]
  48. # L1.sort() # 排序
  49. # L2.sort()
  50. # if L1 == L2: # 比较前Lk-1中的两个项的k-2个元素是否相同,这样才能保证合成后只有k个元素
  51. # retList.append(Lk[i]|Lk[j])
  52. # return retList
  53. def apriori(dataSet, minSupport = 0.5): # 通过循环得出[L1,L2,L3..]频繁项集列表
  54. C1 = createC1(dataSet) # 创造C1
  55. D = list(map(set,dataSet))
  56. L1,supportData = scanD(D, C1, minSupport) 筛选出L1
  57. L = [L1]
  58. k = 2
  59. while (len(L[k-2]) > 0): # 创造Ck
  60. Ck = aprioriGen(L[k-2],k)
  61. Lk, supK = scanD(D,Ck,minSupport)
  62. supportData.update(supK)
  63. L.append(Lk)
  64. k += 1
  65. return L,supportData
  66. if __name__ == "__main__":
  67. dataSet = loadDataSet()
  68. L,suppData = apriori(dataSet)
  69. print(L)

★ 运行结果:

C1 [frozenset({1}), frozenset({2}), frozenset({3}), frozenset({4}), frozenset({5})]
L1 [frozenset({1}), frozenset({3}), frozenset({2}), frozenset({5})]
生成的候选项集 [frozenset({1, 3}), frozenset({1, 2}), frozenset({1, 5}), frozenset({2, 3}), frozenset({3, 5}), frozenset({2, 5})]
筛选出频繁项集 [frozenset({1, 3}), frozenset({2, 3}), frozenset({3, 5}), frozenset({2, 5})]
生成的候选项集 [frozenset({1, 2, 3}), frozenset({1, 3, 5}), frozenset({2, 3, 5})]
筛选出频繁项集 [frozenset({2, 3, 5})]
生成的候选项集 []
筛选出频繁项集 []

★ Apriori算法之挖掘关联规则:

       我们挖掘物品之间的关系,是需要同时满足两个度量的:一个是要满足支持度,二是要满足可信度。因此我们在挖掘关联规则时只需要在频繁项集上挖掘就可以了,因为只有频繁项集才满足最小支持度。在频繁项集上创建所有规则之后,再筛选出满足可信度的规则就算大功告成了,即使如此,一个频繁项集的一个子项所能创造出来的规则,也非常多,计算量也很大,比如L3的一个子项{2,3,5},产生的规则有: {2,3} --> {5},{2,5} --> {3},{3,5} --> {2},{2} --> {3,5},{3} --> {2,5},{5} --> {2,3},由此看到数量也不少,我们需要用Apriori原理进行优化。

       ✿ 前文我们提到,支持度就是概率,可信度就是条件概率。

        假如某一规则为: A --> B,A叫做左件,B叫做右件。它的意思是有人已经买了商品A,那么他还会买商品B的概率较大。这正好对应数学中的条件概率 P(B|A) ,根据定义,该规则的可信度= 支持度{A、B} / 支持度{A},正好与条件概率公式相符,因此:

                                 可信度= P(B|A)  = P(B•A) / P(A)                             ①

      举个例子,对于一个频繁项集的子项{0123},它可以产生的规则如图:

         ✿ 我们根据公式①看出Apriori原理:(跟书上不同哦)

         (1.)  对于右件B,假如它不满足最低可信度,那么它的超集做右件也一定不满足可信度。

        由概率公式:P(B) ≥ P(B的超集),因此 P(B•A) ≥ P(B的超集•A)。所以,假如P(B•A)都不满足可信度要求,那么P(B的超集•A)做分子,更不符合要求。

★ Apriori算法之挖掘关联规则的代码流程:

      ✿ 流程:

       我们先得到每层的频繁项集Lk,然后一层一层的创建关联规则,当然需要 k≥2,假如 k =1,L1= [ {1},{2}...]这怎么创建,1--> Ø,2-->Ø 吗?所以创造规则时忽略 L1,直接从L2开始。

       当k=2的时候,我们没法用Apriori原理预先减去多余的项,如:L2= [ {1,2},{1,3}...],创造出的规则是1-->2,1-->3...,然后通过计算每条规则的可信度来筛选符合要求的规则。

        当 k > 2 时,这时可以用Apriori原理预先减去多余的项了,比如此时k =4,拿出L4的一个频繁项 frozenset({0,1,2,3}),如上图2,假如右件{3}不满足最小可信度要求,根据Apriori原理,那么{3}的超集作为右件的所有规则也不满足最小可信度要求,那么我们就无需计算它的超集了,这就减少了计算量。而如何实现呢?我们只要把不符合可信度条件的右件存起来,然后把他们拆分成单个元素并去重,把可组合的元素集合减去刚才拆分出来的不满足可信度的元素集合,剩下的就是下一轮可组合的集合,然后两两或三三或四四自由组合出新右件,这样的新右件肯定不是刚才那个不满足可信度的右件的超集,因为新右件组合的时候,就没用到那个不满足可信度的右件(因为我们刚才把不满足的右件元素减去了),组合出来后肯定不是它的超集,这就应用了Apriori原理。

       具体实现是:

       我们就以上图2为例:

       频繁项为[frozenset({0,1,2,3})],我们把它拆分成单个元素组成的列表 H = [frozenset({0}),frozenset({1}),frozenset({2}),frozenset({3})],这个H就是待组合成右件的元素集合列表,比如它可以两两

组合,也可以三三组合。我们用H1表示H自由组合后的右件,根据Apriori原理,我们先把H组合成右件只有一个元素的列表(也就是先创建图2的第一层规则),既然右件只有一个元素,那么H1正好等于H,把H1做右件,进行calcConf()筛选满足可信度的规则,假如{3}做右件不满足可信度要求,那么把{3}存到一个列表prunedH中,成为了[frozenset({3)],然后我们在待组合元素列表H减去这个不满足的右件元素{3},不就相当于把这个不满足可信度的右件元素删除了,此时H=[frozenset({0}),frozenset({1}),frozenset({2})],那么接下来的H再两两组合,得到的右件列表肯定不是刚才那个不满足可信度的右件元素的超集,因为刚才把它删除了,再组合肯定没有这个元素出现了,这就是Apriori原理的使用,如此这么循环下去,H这么一直减元素,当H里的元素个数小于需要的右件元素个数时,就停止,比如,H还剩下两个元素了,H=[frozenset({1}),frozenset({2})],而接下来右件元素数需要3个,那么显然它组合不出这样的右件,也就停止了。还有一种停止条件是:当右件元素的个数 = len(原频繁项)的时候,停止,因为根据频繁项创建规则,右件元素的个数最多是len(原频繁项)-1 。

✿  具体代码:

    ✿ 代码理解与疑问:

       (1.) calcConf()改动:

       该函数作用是计算符合可信度要求的规则,并把不符合可信度要求的右件存到一个列表中,函数中的conseq是右件,freqSet是原始频繁项,freqSet-conseq是左件。

      ✿ (2.) rulesFromConseq()函数的疑问:

      该函数是Lk(k>2)频繁项集上创造规则,并应用Apriori原理减少创造的规则,再交给calcConf()去再次筛选。书上源码没看懂,我给出了自己的代码。

      拿过一个频繁项,先把该频繁项拆开,如frozenset({2,3,5}),拆了后就是 H=[frozenset({2}),frozenset({3}),frozenset({5})],这个H就是待组合的元素列表,它可以两两组合,三三组合...,H1是H自由组合后的右件列表,第一次循环右件只有一个元素,因此H1就是H,然后H1的每一项分别做右件。不满足要求的右件的组合保存在一个列表中,待会下次循环我们会把这个不满足可信度的右件的所有组成元素从H中都删去,那么下次H再自由组成H1的时候就不会出现刚才不满足可信度的右件的超集,比如:右件{3}不满足可信度,那么我们把它存到列表中[frozenset({3})],让H减去这个集合后,H=[frozenset({2}),frozenset({5})],此时H再自由组合成H1,就是H1=[frozenset({2,5})],显然没有{3}的超集,这就应用了Apriori原理。随着循环的进行,H就这样一直减不符合可信度的右件的所有组成元素,知道H里面的元素太少,组成不了符合要求的右件了,停止,还有一个停止条件是:当右件元素的个数 = len(原频繁项)的时候,停止,因为根据频繁项创建规则,右件元素的个数最多是len(原频繁项)-1 。

       (3.) generateRules()函数的理解

       该函数作用是产生规则,分为两种情况:一种是可用Apriori原理来减少 Lk(k>2) 频繁项创造出的规则,第二种是不能用Apriori原理去减少 L2 频繁项产生的规则。

  1. def calcConf(freqSet, H, supportData, br1, minConf = 0.7): # 筛选符合可信度要求的规则,并返回符合可信度要求的右件
  2. prunedH = [] # 存储符合可信度的右件
  3. for conseq in H: # conseq就是右件,freqSet是原始频繁项,freqSet-conseq是左件
  4. conf = supportData[freqSet]/supportData[freqSet-conseq] # 计算可信度
  5. if conf>= minConf:
  6. print(freqSet-conseq,"-->",conseq,"\tconf:",conf)
  7. br1.append((freqSet-conseq,conseq,conf))
  8. else:
  9. prunedH.append(conseq) # 不符合可信度的右件添加到列表中
  10. return prunedH
  11. # def rulesFromConseq(freqSet, H, supportData, br1, minConf=0.7): # 原版Apriori原理来减少创造的规则
  12. # m = len(H[0])
  13. # if (len(freqSet)>(m+1)):
  14. # Hmp1 = aprioriGen(H, m+1)
  15. # Hmp1 = calcConf(freqSet, Hmp1, supportData, br1, minConf)
  16. # if (len(Hmp1) >1):
  17. # rulesFromConseq(freqSet, Hmp1, supportData, br1,minConf)
  18. def rulesFromConseq(freqSet, H, supportData, br1, minConf=0.7): # 新版Apriori原理来减少创造的规则
  19. is_find = True # 循环标志
  20. m = 1 # 先创造右件为一个元素的规则
  21. Hmp1 = H # H是初始频繁项分散后的列表,[frozenset({2}),frozenset({3}),frozenset({5)],Hmp1是组合后的右件,因为我们的aprioriGen不能组建只有1个元素的右件,所以右件为1个元素的时候我们直接H赋值过去,当右件元素数是2以上的时候,再用aprioriGen组合出来
  22. while is_find:
  23. if len(freqSet) > m: # 最多循环len(freqSet)-1次,因为右件最多len(freqSet)-1个元素,右件元素的数从1增长到len(freqSet)-1,故最多循环len(freqSet)-1次
  24. if m > 1: # 我们改编的aprioriGen()函数至少产生C2,不能产生C1,因此这里加了if
  25. Hmp1 = aprioriGen(H,m) # H里的元素自由组合成右件,右件的元素个数是m
  26. H_no = calcConf(freqSet, Hmp1, supportData, br1, minConf) # 筛选符合可信度的规则,把不符合的右件存起来
  27. if len(H_no) != 0: # 如果有不满足可信度的右件
  28. H_no = list(set(frozenset([item]) for row in Hmp1 for item in row)) # 我们把列表中的每个元素都分割出来,比如[{2,3},{3,4}] 分割后为[{2},{3},{4}],方便我们再次组合,这里也是Apriori原理的精髓所在,这么操作就是把不满足的右件及其超集提出来,然后后面做减法。
  29. H = list(set(H)-set(H_no)) # 可组合的集合减去不满足可信度的右件的集合
  30. m = m + 1 # 右件个数不断增加,第一次右件元素只有1个,第二次循环右件元素就有两个了
  31. if len(H) < m: # 如果剩余的可自由组合的元素个数少于新右件所需要的元素数,比如就剩两个元素可组合了,想要组成C3作右件,肯定是不可能的,那么结束循环
  32. is_find = False
  33. else: # 如果循环次数达到最大,也结束循环
  34. is_find = False
  35. def generateRules(L, supportData, minConf=0.7): # 产生规则
  36. bigRuleList = []
  37. for i in range(1, len(L)): # 从L2开始创造规则
  38. for freqSet in L[i]:
  39. H1 = [frozenset([item]) for item in freqSet]
  40. if i>1: # L3开始使用Apriori原理
  41. rulesFromConseq(freqSet, H1, supportData, bigRuleList, minConf)
  42. else: # L2不能使用Apriori原理,只能老老实实挨个创造规则
  43. calcConf(freqSet, H1, supportData, bigRuleList, minConf)
  44. return bigRuleList

★ 完整代码及结果:

  1. from numpy import *
  2. import time
  3. def loadDataSet():
  4. return [[1,3,4], [2,3,5], [1,2,3,5], [2,5]]
  5. def createC1(dataSet):
  6. C1 = []
  7. for transaction in dataSet:
  8. for item in transaction:
  9. if not [item] in C1:
  10. C1.append([item])
  11. C1.sort()
  12. return list(map(frozenset,C1))
  13. def scanD(D,Ck,minSupport):
  14. ssCnt = {}
  15. for tid in D:
  16. for can in Ck:
  17. if can.issubset(tid):
  18. if not can in ssCnt:
  19. ssCnt[can] = 1
  20. else:
  21. ssCnt[can] += 1
  22. numItems = float(len(D))
  23. retList = []
  24. supportData = {}
  25. for key in ssCnt:
  26. support = ssCnt[key]/numItems
  27. if support >= minSupport:
  28. # retList.insert(0,key)
  29. retList.append(key)
  30. supportData[key] = support
  31. return retList, supportData
  32. # def aprioriGen(Lk, k):
  33. # retList = []
  34. # lenLk = len(Lk)
  35. # for i in range(lenLk):
  36. # for j in range(i+1, lenLk):
  37. # L1 = list(Lk[i])[:k-2]
  38. # L2 = list(Lk[j])[:k-2]
  39. # L1.sort()
  40. # L2.sort()
  41. # if L1 == L2:
  42. # retList.append(Lk[i]|Lk[j])
  43. # return retList
  44. def aprioriGen(Lk, k):
  45. lenLk = len(Lk)
  46. temp_dict = {}
  47. for i in range(lenLk):
  48. for j in range(i+1, lenLk):
  49. L1 = Lk[i]|Lk[j]
  50. if len(L1) == k:
  51. if not L1 in temp_dict:
  52. temp_dict[L1] = 1
  53. return list(temp_dict)
  54. def apriori(dataSet, minSupport = 0.5):
  55. C1 = createC1(dataSet)
  56. # print("C1",C1)
  57. D = list(map(set,dataSet))
  58. L1,supportData = scanD(D, C1, minSupport)
  59. # print("L1",L1)
  60. L = [L1]
  61. k = 2
  62. while (len(L[k-2]) > 0):
  63. Ck = aprioriGen(L[k-2],k) # 生成候选项集
  64. # print("生成的候选项集",Ck)
  65. Lk, supK = scanD(D,Ck,minSupport) # 按支持度筛选候选项集
  66. # print("筛选出频繁项集",Lk)
  67. supportData.update(supK)
  68. L.append(Lk)
  69. k += 1
  70. return L,supportData
  71. def calcConf(freqSet, H, supportData, br1, minConf = 0.7): # 筛选符合可信度要求的规则,并返回符合可信度要求的右件
  72. prunedH = [] # 存储符合可信度的右件
  73. for conseq in H: # conseq就是右件,freqSet是原始频繁项,freqSet-conseq是左件
  74. conf = supportData[freqSet]/supportData[freqSet-conseq] # 计算可信度
  75. if conf>= minConf:
  76. print(freqSet-conseq,"-->",conseq,"\tconf:",conf)
  77. br1.append((freqSet-conseq,conseq,conf))
  78. else:
  79. prunedH.append(conseq) # 不符合可信度的右件添加到列表中
  80. return prunedH
  81. # def rulesFromConseq(freqSet, H, supportData, br1, minConf=0.7): # 原版Apriori原理来减少创造的规则
  82. # m = len(H[0])
  83. # if (len(freqSet)>(m+1)):
  84. # Hmp1 = aprioriGen(H, m+1)
  85. # Hmp1 = calcConf(freqSet, Hmp1, supportData, br1, minConf)
  86. # if (len(Hmp1) >1):
  87. # rulesFromConseq(freqSet, Hmp1, supportData, br1,minConf)
  88. def rulesFromConseq(freqSet, H, supportData, br1, minConf=0.7): # 新版Apriori原理来减少创造的规则
  89. is_find = True # 循环标志
  90. m = 1 # 先创造右件为一个元素的规则
  91. Hmp1 = H # H是初始频繁项分散后的列表,[frozenset({2}),frozenset({3}),frozenset({5)],Hmp1是组合后的右件,因为我们的aprioriGen不能组建只有1个元素的右件,所以右件为1个元素的时候我们直接H赋值过去,当右件元素数是2以上的时候,再用aprioriGen组合出来
  92. while is_find:
  93. if len(freqSet) > m: # 最多循环len(freqSet)-1次,因为右件最多len(freqSet)-1个元素,右件元素的数从1增长到len(freqSet)-1,故最多循环len(freqSet)-1次
  94. if m > 1: # 我们改编的aprioriGen()函数至少产生C2,不能产生C1,因此这里加了if
  95. Hmp1 = aprioriGen(H,m) # H里的元素自由组合成右件,右件的元素个数是m
  96. H_no = calcConf(freqSet, Hmp1, supportData, br1, minConf) # 筛选符合可信度的规则,把不符合的右件存起来
  97. if len(H_no) != 0: # 如果有不满足可信度的右件
  98. H_no = list(set(frozenset([item]) for row in Hmp1 for item in row)) # 我们把列表中的每个元素都分割出来,比如[{2,3},{3,4}] 分割后为[{2},{3},{4}],方便我们再次组合,这里也是Apriori原理的精髓所在,这么操作就是把不满足的右件及其超集提出来,然后后面做减法。
  99. H = list(set(H)-set(H_no)) # 可组合的集合减去不满足可信度的右件的集合
  100. m = m + 1 # 右件个数不断增加,第一次右件元素只有1个,第二次循环右件元素就有两个了
  101. if len(H) < m: # 如果剩余的可自由组合的元素个数少于新右件所需要的元素数,比如就剩两个元素可组合了,想要组成C3作右件,肯定是不可能的,那么结束循环
  102. is_find = False
  103. else: # 如果循环次数达到最大,也结束循环
  104. is_find = False
  105. def generateRules(L, supportData, minConf=0.7): # 产生规则
  106. bigRuleList = []
  107. for i in range(1, len(L)): # 从L2开始创造规则
  108. for freqSet in L[i]:
  109. H1 = [frozenset([item]) for item in freqSet]
  110. if i>1: # L3开始使用Apriori原理
  111. rulesFromConseq(freqSet, H1, supportData, bigRuleList, minConf)
  112. else: # L2不能使用Apriori原理,只能老老实实挨个创造规则
  113. calcConf(freqSet, H1, supportData, bigRuleList, minConf)
  114. return bigRuleList
  115. if __name__ == "__main__":
  116. dataSet = loadDataSet()
  117. begin_time = time.time()
  118. L,suppData = apriori(dataSet)
  119. rules = generateRules(L, suppData, minConf=0.5)
  120. end_time = time.time()
  121. print("程序花费时间{}秒".format(end_time-begin_time))
  122. # print(L)
  123. # print(suppData)

✿ 运行结果:

frozenset({3}) --> frozenset({1})     conf: 0.6666666666666666
frozenset({1}) --> frozenset({3})     conf: 1.0
frozenset({3}) --> frozenset({2})     conf: 0.6666666666666666
frozenset({2}) --> frozenset({3})     conf: 0.6666666666666666
frozenset({5}) --> frozenset({3})     conf: 0.6666666666666666
frozenset({3}) --> frozenset({5})     conf: 0.6666666666666666
frozenset({5}) --> frozenset({2})     conf: 1.0
frozenset({2}) --> frozenset({5})     conf: 1.0
frozenset({3, 5}) --> frozenset({2})     conf: 1.0
frozenset({2, 5}) --> frozenset({3})     conf: 0.6666666666666666
frozenset({2, 3}) --> frozenset({5})     conf: 1.0
frozenset({5}) --> frozenset({2, 3})     conf: 0.6666666666666666
frozenset({2}) --> frozenset({3, 5})     conf: 0.6666666666666666
frozenset({3}) --> frozenset({2, 5})     conf: 0.6666666666666666
程序花费时间0.001077890396118164秒

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号