4.亲和性分析算法

# -*- coding: utf-8 -*-
"""
Created on Thu Sep 27 10:59:39 2018

@author: asus
"""
#4 用亲和性分析方法推荐电影

#4.1 亲和性分析
#亲和性分析用来寻找出两个对象同时出现的情况。
#应用场景:欺诈检测;顾客区分;软件优化;产品推荐。

#4.1.1 亲和性分析算法
#Apriori算法
#其他算法有Eclat和频繁项集挖掘算法(FP-growth)

#4.1.2 选择参数
#挖掘亲和性分析所用的关联规则之前,我们先用Apriori算法生成频繁项集。接着,通过检测频繁
#项集中前提和结论的组合,生成关联规则。

#4.2 电影推荐问题
#4.2.1 获取数据集
#美国明尼苏达大学的Grouplens研究团队公开的一系列用于测试推荐算法的数据集。
#电影评分数据,http://grouplens.org/datasets/movielens/
import pandas as pd

#4.2.2 用pandas加载数据
#把分隔符设置为制表符,不要把第一行作为表头(header=None),设置好各类的名称。
all_ratings = pd.read_csv("u.data", delimiter="\t", header=None,
                          names = ["UserID", "MovieID", "Rating", "Datetime"])
#可以用下面的代码解析时间戳数据。
all_ratings["Datetime"] = pd.to_datetime(all_ratings['Datetime'], unit='s')
all_ratings[:5]

#4.2.3 稀疏数据格式
#序号为0的那一行表示,用户(UserID)#196在1997年12月4日为电影(MovieID)#242打了3分(满
#分是5分)
#任何没有出现在数据集中的用户和电影组合表示它们实际上是不存在的。

#4.3 Apriori算法的实现
#目标:如果用户喜欢某些电影,那么他们也会喜欢这部电影。

#首先确定用户是否喜欢某一部电影,创建新特征Favorable,喜欢为True。
all_ratings["Favorable"] = all_ratings["Rating"] > 3
all_ratings[10:15]
#从数据集中选取一部分数据用作训练集,这样能有效减少搜索空间,提升Apriori算法的速度。
#取前200名用户的打分数据。
ratings = all_ratings[all_ratings['UserID'].isin(range(200))]
#新建一个数据集,只包括用户喜欢的某部电影的数据行。
favorable_ratings = ratings[ratings["Favorable"]]
#在生成项集时,需要搜索用户喜欢的电影。
favorable_reviews_by_users = dict((k, frozenset(v.values))
                                    for k, v in favorable_ratings.
                                    groupby("UserID")["MovieID"])
#上面的代码把v.values存储为frozenset,便于快速判断用户是否为了某部电影打过分。对于这
#种操作集合比列表快。
#最后,创建一个数据框,以便了解每部电影的影迷数量。
num_favorable_by_movie = ratings[["MovieID", "Favorable"]].\
                                groupby("MovieID").sum() 
#最受欢迎的五部电影
num_favorable_by_movie.sort_values("Favorable", ascending=False)[:5]

#4.3.1 Apriori算法
#创建一个函数,它接收新发现的频繁项集,创建超集,检测频繁程度
'''
favorable_reviews_by_users: 用户打分情况的集合
k_1_itemsets: 上一个频繁项集
min_support:最小支持度
返回值格式:
    dict( 频繁项集    支持度 )
'''
frequent_itemsets = {}
min_support = 50 #最小支持度
#为每一部电影生成只包含自己的项集,检测它是否能够频繁。
frequent_itemsets[1] = dict((frozenset((movie_id,)),row["Favorable"])\
                    for movie_id, row in num_favorable_by_movie.iterrows()
                    if row["Favorable"] > min_support)
#接收新发现的频繁项集,创建超集,检测频繁程度
from collections import defaultdict
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])
#创建循环,运行Apriori算法,存储算法运行过程中发现的新项集。
for k in range(2, 20):
    cur_fequent_itemsets = find_frequent_itemsets(favorable_reviews_by_users,
                                                  frequent_itemsets[k-1],
                                                  min_support)
    frequent_itemsets[k] = cur_fequent_itemsets
    if len(cur_fequent_itemsets) == 0:
        print("没有找到长度为{}的频繁项集".format(k))
        break
    else:
        print("I foud {} frequent itemsets of length {}".
              format(len(cur_fequent_itemsets), k))
#删除长度为1的项集
del frequent_itemsets[1]

#4.4 抽取关联规则
#可以从频繁项集中抽取出关联规则,把其中几部电影作为前提,另一部电影作为结论,组成如下
#形式的规则:如果用户喜欢前提中的所有电影,那么他们也会喜欢结论中的电影。

#遍历不同长度的频繁项集,为每个项集生成规则。
candidate_rules = []
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(candidate_rules[:5])
#在上述这些规则中,第一部分(forzenset)是作为规则前提的电影编号,后面的数字表示作为
#结论的电影编号。

#接下来,计算置信度。
#先创建两个字典,用来存储规则应验(正例)和规则不使用(反例)的次数。
correct_counts = defaultdict(int)
incorrect_counts = defaultdict(int)
#遍历所有用户及其喜欢的电影数据,在这个过程中遍历每条关联规则。
#测试每条规则的前提对用户是否适用。如果前提符合,看一下用户是否喜欢结论中的电影,如果
#是的话,规则适用,反之,规则不适用。
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}
#对置信度排序后,输出置信度最高的前五条规则
from operator import itemgetter
sorted_confidence = sorted(rule_confidence.items(), 
                           key=itemgetter(1), reverse=True)
for index in range(5):
    print("Rule #{0}".format(index + 1))
    (premise, conclusion) = sorted_confidence[index][0]
    print("Rule: If a person recommends {0} they will also recommend {1}".
          format(premise, conclusion))
    print(" - Confidence:\
            {0:.3f}".format(rule_confidence[(premise, conclusion)]))
    print("")
#输出结果中只有电影编号,没有电影名字。在u.item文件中。间隔为|。
movie_name_data = pd.read_csv('u.item', delimiter='|',
                                  header=None, encoding='mac-roman')
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"]
#创建一个电影编号获取名称的函数。
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

#调整之前的代码
for index in range(5):
    print("Rule #{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("Rule: If a person recommends {0} they will also recommend {1}".
          format(premise_names, conclusion_name))
    print(" - Confidence:\
            {0:.3f}".format(rule_confidence[(premise, conclusion)]))
    print("")

#测试集是训练集剩下的数据
test_dataset = all_ratings[~all_ratings["UserID"].isin(range(200))]
test_favorable = test_dataset[test_dataset["Favorable"]]
test_favorable_by_users = dict((k, frozenset(v.values)) for k, v
                               in test_favorable.groupby("UserID")["MovieID"])
#接着计算规则应验的数量
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
#接下来,计算应验规则的置信度
test_confidence = {candidate_rule: correct_counts[candidate_rule]/
                   float(correct_counts[candidate_rule] + 
                         incorrect_counts[candidate_rule])
                   for candidate_rule in candidate_rules}

#最后,输出用电影名称而不是电影编号表示的最佳关联规则。
for index in range(5):
    print("Rule #{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("Rule: If a person recommends {0} they will also recommend {1}".
          format(premise_names, conclusion_name))
    print(" - Confidence:\
            {0:.3f}".format(rule_confidence[(premise, conclusion)]))
    print(" - Confidence:\
            {0:.3f}".format(test_confidence[(premise, conclusion)]))
    print("")




猜你喜欢

转载自blog.csdn.net/qq_39124646/article/details/82996408