python数据挖掘(5.Apriori算法)

六眼飞鱼酱① 提交于 2019-12-10 04:22:23

第一章我们介绍了最基础的亲和性分析,尝试了所有的规则计算了所有的置信度和支持度,但是这个方法效率不高而且我们使用的数据集只有5种商品,但是实际生活中即使是小商店的商品也会超过百种,而网店商品的种类则可能更多,依旧使用第一章的亲和性分析,那么随着商品数量的增加,计算量和计算的时间也会急剧增加,所以需要一个聪明的算法来解决这个问题

Apriori算法

Apriori算法诗一个景点的亲和性分析算法,他只从数据集中频繁出现的商品中选取共同出现的商品组成频繁项集,避免上述复杂度呈指数级增长的问题,一旦找到了频繁项集,生成关联规则就很容易了。
Apriori算法首先保证规则在数据集中有足够的支持度,最重要的一个参数就是最小支持度比如要生成商品A B的频繁项集(A,B)要求支持度至少为30,那么A,B都必须至少在数据集中出现30次,更大的频繁项集也要最受这个约定。

这一章我们通过电影推荐的问题来举例。
数据集地址在文章开头

In [1]: import numpy as np                                                      

In [2]: import pandas as pd                                                     

In [3]: all_ratings = pd.read_csv('/Users/gn/scikit--learn/ml-100k/u.data',delim
   ...: iter="\t", header=None, names = ["UserID", "MovieID", "Rating", "Datetim
   ...: e"])                                                                    

In [4]: all_ratings.head()                                                      
Out[4]: 
   UserID  MovieID  Rating   Datetime
0     196      242       3  881250949
1     186      302       3  891717742
2      22      377       1  878887116
3     244       51       2  880606923
4     166      346       1  886397596

In [5]: all_ratings["Datetime"] = pd.to_datetime(all_ratings['Datetime'],unit='s
   ...: ')                                                                      

In [6]: all_ratings["Favorable"] = all_ratings['Rating'] > 3                                                            

In [7]: ratings  = all_ratings[all_ratings['UserID'].isin(range(200))]                                                  

In [8]: ratings.head()                                                                                                  
Out[8]: 
   UserID  MovieID  Rating            Datetime  Favorable
0     196      242       3 1997-12-04 15:55:49      False
1     186      302       3 1998-04-04 19:22:22      False
2      22      377       1 1997-11-07 07:18:36      False
4     166      346       1 1998-02-02 05:33:16      False
6     115      265       2 1997-12-03 17:51:28      False

In [9]: favorable_ratings = ratings[ratings["Favorable"]]                                                               

In [10]: favorable_ratings.groupby("UserID")["MovieID"]                                                                 
Out[10]: <pandas.core.groupby.generic.SeriesGroupBy object at 0x11583d9b0>

In [11]: favorable_reviews_by_users = dict((k, frozenset(v.values)) for k, v in favorable_ratings.groupby("UserID")["MovieID"])                         

In [12]: num_favorable_by_movie = ratings[["MovieID", "Favorable"]].groupby("MovieID").sum()                                                            

In [13]: num_favorable_by_movie.sort_values('Favorable',ascending=False).head()                                                                         
Out[13]: 
         Favorable
MovieID           
50           100.0
100           89.0
258           83.0
181           79.0
174           74.0

In [14]: from collections import defaultdict                                                                                                            

In [15]: frequent_itemsets = {}                                                                                                                         

In [16]: min_support = 50                                                                                                                               

In [17]: frequent_itemsets[1] = dict((frozenset((movie_id,)), row["Favorable"]) for movie_id,row in num_favorable_by_movie[num_favorable_by_movie['Favorable']>50].iterrows())          

In [18]: import sys                                                                                                                                                                                      

In [19]: print("有 {} 部电影 获得超过 {} 个好评".format(len(frequent_itemsets[1]), min_support))                                                                                                         
有 16 部电影 获得超过 50 个好评

上线一段代码我们读取了数据,获得了一些基本的信息。
下面我们来寻找‘频繁项集’

In [20]: def find_frequent_itemsets(favorable_reviews_by_users, k_1_itemsets, min_support): 
    ...:     counts = defaultdict(int) 
    ...:     for user,reviews in favorable_reviews_by_users.items(): 
    ...:         for itemset in k_1_itemsets: 
    ...:             if itemset.issubset(reviews): 
    ...:                 for other_reviewed_movie in reviews - itemset: 
    ...:                     current_superset = itemset | frozenset((other_reviewed_movie,)) 
    ...:                     counts[current_superset] += 1 
    ...:     return dict([(itemset, frequency) for itemset, frequency in counts.items() if frequency >= min_support]) 
    ...:                                                                                                                                                                                                 

In [21]: for k in range(2, 20): 
    ...:     cur_frequent_itemsets = find_frequent_itemsets(favorable_reviews_by_users, frequent_itemsets[k-1],min_support) 
    ...:     if len(cur_frequent_itemsets) == 0: 
    ...:         print("未发现频数为{}的频繁项集".format(k)) 
    ...:         sys.stdout.flush() 
    ...:         break 
    ...:     else: 
    ...:         print("发现 {} 个长度为 {}的频繁项集".format(len(cur_frequent_itemsets), k)) 
    ...:         sys.stdout.flush() 
    ...:         frequent_itemsets[k] = cur_frequent_itemsets 
    ...:                                                                                                                                                                                                 
发现 93 个长度为 2的频繁项集
发现 295 个长度为 3的频繁项集
发现 593 个长度为 4的频繁项集
发现 785 个长度为 5的频繁项集
发现 677 个长度为 6的频繁项集
发现 373 个长度为 7的频繁项集
发现 126 个长度为 8的频繁项集
发现 24 个长度为 9的频繁项集
发现 2 个长度为 10的频繁项集
未发现频数为11的频繁项集

Apriori算法结束后我们得到了一些列频繁项集,这还不算是真正意义上的关联规则,但是很接近。频繁相机是一组达到最小支持度的项目,而关联规则由前提和结论组成。
我们从拼房相机中抽取出关联规则,把其中几部电影作为前提,另一部电影作为结论生成如下形式的规则:“如果用户喜欢前提中的电影,那么他们也会喜欢结论中的电影”

下面我们看一下哪些规则的置信度比较高

In [22]: del frequent_itemsets[1]                                                                                                                                                                        

In [23]: candidate_rules = []                                                                                                                                                                            

In [24]: for itemset_length, itemset_counts in frequent_itemsets.items(): 
    ...:     for itemset in itemset_counts.keys(): 
    ...:         for conclusion in itemset: 
    ...:             premise = itemset - set((conclusion,)) 
    ...:             candidate_rules.append((premise, conclusion)) 
    ...: print("生成 {} 条规则 ".format(len(candidate_rules)))                                                                                                                                           
生成 15285 条规则 

In [25]: correct_counts = defaultdict(int)                                                                                                                                                               

In [26]: incorrect_counts = defaultdict(int)                                                                                                                                                             

In [27]: for user, reviews in favorable_reviews_by_users.items(): 
    ...:     for candidate_rule in candidate_rules: 
    ...:         premise, conclusion = candidate_rule 
    ...:         if premise.issubset(reviews): 
    ...:             if conclusion in reviews: 
    ...:                 correct_counts[candidate_rule] += 1 
    ...:             else: 
    ...:                 incorrect_counts[candidate_rule] += 1 
    ...: rule_confidence = {candidate_rule: correct_counts[candidate_rule] / float(correct_counts[candidate_rule] + incorrect_counts[candidate_rule]) 
    ...:               for candidate_rule in candidate_rules}                                                                                                                                            

                                                                                                                                                                                      

In [28]: min_confidence = 0.9                                                                                                                                                                            

In [29]: rule_confidence = {rule: confidence for rule, confidence in rule_confidence.items() if confidence > min_confidence} 
    ...: print(len(rule_confidence))                                                                                                                                                                     
5152

In [30]: from operator import itemgetter                                                                                                                                                                 

In [31]: sorted_confidence = sorted(rule_confidence.items(), key=itemgetter(1), reverse=True)                                                                                                            

In [32]: for index in range(5): 
    ...:     print("规则 #{0}".format(index + 1)) 
    ...:     (premise, conclusion) = sorted_confidence[index][0] 
    ...:     print("规则: 如果有人喜欢电影 {0} 那么他们同样喜欢电影 {1}".format(premise, conclusion)) 
    ...:     print(" - 置信度: {0:.3f}".format(rule_confidence[(premise, conclusion)])) 
    ...:     print("") 
    ...:                                                                                                                                                                                                 
规则 #1
规则: 如果有人喜欢电影 frozenset({98, 181}) 那么他们同样喜欢电影 50
 - 置信度: 1.000

规则 #2
规则: 如果有人喜欢电影 frozenset({172, 79}) 那么他们同样喜欢电影 174
 - 置信度: 1.000

规则 #3
规则: 如果有人喜欢电影 frozenset({258, 172}) 那么他们同样喜欢电影 174
 - 置信度: 1.000

规则 #4
规则: 如果有人喜欢电影 frozenset({1, 181, 7}) 那么他们同样喜欢电影 50
 - 置信度: 1.000

规则 #5
规则: 如果有人喜欢电影 frozenset({1, 172, 7}) 那么他们同样喜欢电影 174
 - 置信度: 1.000

我们一共生成了15285条规则,之后创建两个字典,一个存储符合规则(correct_counts),另一个储存不符合规则(incorrect_counts),循环我们的数据计算符合和不符合规则的数量,并且计算置信度,最后我们拿出了置信度前五个的规则。

现在结果只显示了电影编号,我们的数据中还有一部分电影信息。下面我们将这部分加进来

In [41]: movie_name_data = pd.read_csv('/Users/gn/scikit--learn/ml-100k/u.item',delimiter='|',header=None,encoding='mac-roman')                                                                          

In [42]: movie_name_data.head()                                                                                                                                                                          
Out[42]: 
   0                  1            2   3                                                  4   5   6   7   8   9   10  11  12  13  14  15  16  17  18  19  20  21  22  23
0   1   Toy Story (1995)  01-Jan-1995 NaN  http://us.imdb.com/M/title-exact?Toy%20Story%2...   0   0   0   1   1   1   0   0   0   0   0   0   0   0   0   0   0   0   0
1   2   GoldenEye (1995)  01-Jan-1995 NaN  http://us.imdb.com/M/title-exact?GoldenEye%20(...   0   1   1   0   0   0   0   0   0   0   0   0   0   0   0   0   1   0   0
2   3  Four Rooms (1995)  01-Jan-1995 NaN  http://us.imdb.com/M/title-exact?Four%20Rooms%...   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   1   0   0
3   4  Get Shorty (1995)  01-Jan-1995 NaN  http://us.imdb.com/M/title-exact?Get%20Shorty%...   0   1   0   0   0   1   0   0   1   0   0   0   0   0   0   0   0   0   0
4   5     Copycat (1995)  01-Jan-1995 NaN  http://us.imdb.com/M/title-exact?Copycat%20(1995)   0   0   0   0   0   0   1   0   1   0   0   0   0   0   0   0   1   0   0

In [43]: movie_name_data.columns = ["MovieID", "Title", "Release Date", "Video Release", "IMDB", "<UNK>", "Action", "Adventure", 
    ...:                            "Animation", "Children's", "Comedy", "Crime", "Documentary", "Drama", "Fantasy", "Film-Noir", 
    ...:                            "Horror", "Musical", "Mystery", "Romance", "Sci-Fi", "Thriller", "War", "Western"]                                                                                   

In [44]: movie_name_data.head()                                                                                                                                                                          
Out[44]: 
   MovieID              Title Release Date  Video Release                                               IMDB  <UNK>  Action  ...  Musical  Mystery  Romance  Sci-Fi  Thriller  War  Western
0        1   Toy Story (1995)  01-Jan-1995            NaN  http://us.imdb.com/M/title-exact?Toy%20Story%2...      0       0  ...        0        0        0       0         0    0        0
1        2   GoldenEye (1995)  01-Jan-1995            NaN  http://us.imdb.com/M/title-exact?GoldenEye%20(...      0       1  ...        0        0        0       0         1    0        0
2        3  Four Rooms (1995)  01-Jan-1995            NaN  http://us.imdb.com/M/title-exact?Four%20Rooms%...      0       0  ...        0        0        0       0         1    0        0
3        4  Get Shorty (1995)  01-Jan-1995            NaN  http://us.imdb.com/M/title-exact?Get%20Shorty%...      0       1  ...        0        0        0       0         0    0        0
4        5     Copycat (1995)  01-Jan-1995            NaN  http://us.imdb.com/M/title-exact?Copycat%20(1995)      0       0  ...        0        0        0       0         1    0        0

[5 rows x 24 columns]

In [45]: def get_movie_name(movie_id): 
    ...:     title_object = movie_name_data[movie_name_data["MovieID"] == movie_id]["Title"] 
    ...:     title = title_object.values[0] 
    ...:     return title 
    ...:                                                                                                                                                                                                 

In [46]: for index in range(5): 
    ...:     print("规则 #{0}".format(index + 1)) 
    ...:     (premise, conclusion) = sorted_confidence[index][0] 
    ...:     premise_names = ", ".join(get_movie_name(idx) for idx in premise) 
    ...:     conclusion_name = get_movie_name(conclusion) 
    ...:     print("规则: 如果有人喜欢电影 {0}那么他们同样喜欢电影 {1}".format(premise_names, conclusion_name)) 
    ...:     print(" - 置信度: {0:.3f}".format(rule_confidence[(premise, conclusion)])) 
    ...:     print("") 
    ...:                                                                                                                                                                                                 
规则 #1
规则: 如果有人喜欢电影 Silence of the Lambs, The (1991), Return of the Jedi (1983)那么他们同样喜欢电影 Star Wars (1977)
 - 置信度: 1.000

规则 #2
规则: 如果有人喜欢电影 Empire Strikes Back, The (1980), Fugitive, The (1993)那么他们同样喜欢电影 Raiders of the Lost Ark (1981)
 - 置信度: 1.000

规则 #3
规则: 如果有人喜欢电影 Contact (1997), Empire Strikes Back, The (1980)那么他们同样喜欢电影 Raiders of the Lost Ark (1981)
 - 置信度: 1.000

规则 #4
规则: 如果有人喜欢电影 Toy Story (1995), Return of the Jedi (1983), Twelve Monkeys (1995)那么他们同样喜欢电影 Star Wars (1977)
 - 置信度: 1.000

规则 #5
规则: 如果有人喜欢电影 Toy Story (1995), Empire Strikes Back, The (1980), Twelve Monkeys (1995)那么他们同样喜欢电影 Raiders of the Lost Ark (1981)
 - 置信度: 1.000

我们可以拿评估分类算法的那一套来用,训练前,流出一部分数据用作测试,评估发现的规则在测试集上的表现。和之前是差不多的。

In [47]: test_dataset = all_ratings[~all_ratings['UserID'].isin(range(200))]                                                                                                                             

In [48]: test_favorable = test_dataset[test_dataset["Favorable"]]                                                                                                                                        

In [49]: test_favorable_by_users = dict((k, frozenset(v.values)) for k, v in test_favorable.groupby("UserID")["MovieID"])                                                                                

In [50]: test_dataset.head()                                                                                                                                                                             
Out[50]: 
    UserID  MovieID  Rating            Datetime  Favorable
3      244       51       2 1997-11-27 05:02:03      False
5      298      474       4 1998-01-07 14:20:06       True
7      253      465       5 1998-04-03 18:34:27       True
8      305      451       3 1998-02-01 09:20:17      False
11     286     1014       5 1997-11-17 15:38:45       True

In [51]: correct_counts = defaultdict(int) 
    ...: incorrect_counts = defaultdict(int) 
    ...: for user, reviews in test_favorable_by_users.items(): 
    ...:     for candidate_rule in candidate_rules: 
    ...:         premise, conclusion = candidate_rule 
    ...:         if premise.issubset(reviews): 
    ...:             if conclusion in reviews: 
    ...:                 correct_counts[candidate_rule] += 1 
    ...:             else: 
    ...:                 incorrect_counts[candidate_rule] += 1 
    ...:                                                                                                                                                                                                 

In [52]: test_confidence = {candidate_rule: correct_counts[candidate_rule] / float(correct_counts[candidate_rule] + incorrect_counts[candidate_rule]) 
    ...:                    for candidate_rule in rule_confidence}                                                                                                                                       

In [53]: sorted_test_confidence = sorted(test_confidence.items(), key=itemgetter(1), reverse=True)                                                                                                       

In [54]: for index in range(10): 
    ...:     print("规则 #{0}".format(index + 1)) 
    ...:     (premise, conclusion) = sorted_confidence[index][0] 
    ...:     premise_names = ", ".join(get_movie_name(idx) for idx in premise) 
    ...:     conclusion_name = get_movie_name(conclusion) 
    ...:     print("规则:如果有人喜欢电影  {0} 那么他们同样喜欢电影 {1}".format(premise_names, conclusion_name)) 
    ...:     print(" - 训练 置信度: {0:.3f}".format(rule_confidence.get((premise, conclusion), -1))) 
    ...:     print(" - 测试 置信度: {0:.3f}".format(test_confidence.get((premise, conclusion), -1))) 
    ...:     print("") 
    ...:                                                                                                                                                                                                 
规则 #1
规则:如果有人喜欢电影  Silence of the Lambs, The (1991), Return of the Jedi (1983) 那么他们同样喜欢电影 Star Wars (1977)
 - 训练 置信度: 1.000
 - 测试 置信度: 0.936

规则 #2
规则:如果有人喜欢电影  Empire Strikes Back, The (1980), Fugitive, The (1993) 那么他们同样喜欢电影 Raiders of the Lost Ark (1981)
 - 训练 置信度: 1.000
 - 测试 置信度: 0.876

规则 #3
规则:如果有人喜欢电影  Contact (1997), Empire Strikes Back, The (1980) 那么他们同样喜欢电影 Raiders of the Lost Ark (1981)
 - 训练 置信度: 1.000
 - 测试 置信度: 0.841

规则 #4
规则:如果有人喜欢电影  Toy Story (1995), Return of the Jedi (1983), Twelve Monkeys (1995) 那么他们同样喜欢电影 Star Wars (1977)
 - 训练 置信度: 1.000
 - 测试 置信度: 0.932

规则 #5
规则:如果有人喜欢电影  Toy Story (1995), Empire Strikes Back, The (1980), Twelve Monkeys (1995) 那么他们同样喜欢电影 Raiders of the Lost Ark (1981)
 - 训练 置信度: 1.000
 - 测试 置信度: 0.903

规则 #6
规则:如果有人喜欢电影  Pulp Fiction (1994), Toy Story (1995), Star Wars (1977) 那么他们同样喜欢电影 Raiders of the Lost Ark (1981)
 - 训练 置信度: 1.000
 - 测试 置信度: 0.816

规则 #7
规则:如果有人喜欢电影  Pulp Fiction (1994), Toy Story (1995), Return of the Jedi (1983) 那么他们同样喜欢电影 Star Wars (1977)
 - 训练 置信度: 1.000
 - 测试 置信度: 0.970

规则 #8
规则:如果有人喜欢电影  Toy Story (1995), Silence of the Lambs, The (1991), Return of the Jedi (1983) 那么他们同样喜欢电影 Star Wars (1977)
 - 训练 置信度: 1.000
 - 测试 置信度: 0.933

规则 #9
规则:如果有人喜欢电影  Toy Story (1995), Empire Strikes Back, The (1980), Return of the Jedi (1983) 那么他们同样喜欢电影 Star Wars (1977)
 - 训练 置信度: 1.000
 - 测试 置信度: 0.971

规则 #10
规则:如果有人喜欢电影  Pulp Fiction (1994), Toy Story (1995), Shawshank Redemption, The (1994) 那么他们同样喜欢电影 Silence of the Lambs, The (1991)
 - 训练 置信度: 1.000
 - 测试 置信度: 0.794

从大量的电影打分数据中找到可以用于电影推荐的关联规则,整个过程分为2大阶段,首先借助Apriori算法寻找频繁项集,然后生成相关规则,再计算置信度。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!