本文是《python数据挖掘与入门实践》第四章“用亲和性分析方法推荐电影”的笔记
目的
学习Apriori算法,实现更精准的电影推荐服务,向潜在的客户推荐他们可能喜欢的电影
向网站用户提供多样化的服务或投放定向广告
根据基因寻找有亲缘关系的人
步骤
数据集获取&清洗
Apriori 算法
2.1 Apriori 算法原理
2.2 Apriori 算法步骤
2.3 Apriori 算法实现
抽取关联规则
3.1 规则代码
3.2 规则置信度
评估
总代码
1. 数据集获取&清洗
import os
import pandas as pd
# 文件夹地址
data_folder = '/Users/chengkai/Desktop/file/learn/project/用亲和性分析方法推荐电影/dataset/ml-100k'
# 文件地址
ratings_filename = data_folder+'/u.data'
image.png
打开u.data 长这样,没有标题行,用Tap键分隔,最后的数字是时间,所以需要数据整理
# 给u.data 加标题行
all_ratings = pd.read_csv(ratings_filename, delimiter="\t", header=None, names= ["UserID","MovieID","Rating","Datetime"])
# 改变时间显示
all_ratings["Datetime"] = pd.to_datetime(all_ratings['Datetime'], unit='s')
print(all_ratings[:5])
image.png
修改完后长这样,是不是好看很多哈~
解读第0行:ID为196的用户,在1997年12月04日15点55分49秒,给ID为242的电影的评分为3(满分为5分)
2. Apriori 算法的实现
2.1 Apriori 算法原理
Apriori 算法:我个人理解是,统计某些商品一起出售的概率,如果消费者买了产品A,那么很有可能买产品B,所以说我们找到A与B之间的关联,就可以给买A的客户推荐B,从而大大增加潜在的收益。
用流程图直观的解释下吧
a) 序号为1,2,3,4的客户买了各种商品组合
b) 找到各商A,B,C,D,E,出现的频次
c) 删除出现频次低于50%的
d) 剩余的俩俩组合
e) 删除出现频次低于50%的
f) 剩余的三三组合
g) 删除频率低于50%的
h) 最后找到组合{B,C,E},{A,C},{B,C},{B,E},{C,E},有关联
image.png
2.2 Apriori 算法步骤
定义一个规则:
如果用户喜欢某些电影,那么他们也会喜欢这部电影
拓展:喜欢某几部电影的用户,是否喜欢另一部电影
# 添加一个特征,即评分大于3的定义为喜欢,小于3的定义为不喜欢
all_ratings["Favorable"] = all_ratings["Rating"]>3
print(all_ratings[5:10])
image.png
# 提取前200个数据作为训练集
ratings = all_ratings[all_ratings['UserID'].isin(range(200))]
# 新建一个数据集,只包括用户喜欢某部电影的数据行
favorable_ratings = ratings[ratings["Favorable"]]
# 统计每个用户各喜欢哪些电影,按照UserID进行分组,并遍历每个用户看过的每一部电影
favorable_reviews_by_users = dict((k, frozenset(v.values)) for k,v in favorable_ratings.groupby("UserID")["MovieID"])
output
{1: frozenset({1, 3, 6, 7, 9, 12, 13,...,268, 269, 270}), 2: frozenset({257, 1, 13, 14, 269,...127, 255}),...199: frozenset({258, 7, 313, ... , 221, 286})}
编号为1 的观众,喜欢的电影有1,3,6。。。268,269,270。
# 再新建一个数据集,统计每部电影获得的喜欢数(点赞个数)
num_favorable_by_movie = ratings[["MovieID", "Favorable"]].groupby("MovieID").sum()
# 看一下前5位的点赞统计
print(num_favorable_by_movie.sort_values("Favorable", ascending=False)[:5])
image.png
2.3 Apriori 算法实现
# 初始化一个字典
frequent_itemsets = {}
# 最小点赞数
min_support = 50
# 为每一部电影生成只包含它自己的项目,检测它是否够频繁
# numnum_favorable_by_movie.iterrows() 对数据进行遍历
frequent_itemsets[1] = dict((frozenset((movie_id,)),row["Favorable"]) for movie_id, row in num_favorable_by_movie.iterrows() if row["Favorable"] > min_support )
print(frequent_itemsets)
output
{1: {frozenset({1}): 66.0, frozenset({7}): 67.0, frozenset({9}): 53.0, frozenset({50}): 100.0, frozenset({56}): 67.0, frozenset({64}): 58.0, frozenset({79}): 58.0, frozenset({98}): 70.0, frozenset({100}): 89.0, frozenset({127}): 70.0, frozenset({172}): 59.0, frozenset({174}): 74.0, frozenset({181}): 79.0, frozenset({258}): 83.0, frozenset({286}): 59.0, frozenset({313}): 60.0}}
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():
# 遍历上一个的项集,判断itemset是不是每个用户喜欢的电影的子集
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
print(counts)
# 函数最后检测达到支持度要求的项集,看它的频繁程度够不够,并返回其中的频繁项集
return dict([(itemset, frequency) for itemset, frequency in counts.items() if frequency >= min_support])
output (print(counts)
defaultdict(, {frozenset({1, 3}): 3, frozenset({1, 6}): 3, frozenset({1, 7}): 62,...)
for k in range(2, 20):
# 实例化函数,k表示即将发现的频繁项集的长度,用键k-1可以从frequent_itemsets字典中获取刚发现的频繁项集。
cur_frequent_itemsets = find_frequent_itemsets(favorable_reviews_by_users, frequent_itemsets[k-1], min_support)
# 新发现的频繁项集以长度为键,将其保存到字典中
frequent_itemsets[k] = cur_frequent_itemset