日萌社
人工智能AI:Keras PyTorch MXNet TensorFlow PaddlePaddle 深度学习实战(不定时更新)
1.4 案例--基于协同过滤的电影推荐
学习目标
- 应用基于用户的协同过滤实现电影评分预测
- 应用基于物品的协同过滤实现电影评分预测
1 User-Based CF 预测电影评分
-
数据集下载
- 下载地址:MovieLens Latest Datasets Small
- 建议下载ml-latest-small.zip,数据量小,便于我们单机使用和运行
-
加载ratings.csv,转换为用户-电影评分矩阵并计算用户之间相似度
import os import pandas as pd import numpy as np DATA_PATH = "./datasets/ml-latest-small/ratings.csv" dtype = {"userId": np.int32, "movieId": np.int32, "rating": np.float32} # 加载数据,我们只用前三列数据,分别是用户ID,电影ID,已经用户对电影的对应评分 ratings = pd.read_csv(data_path, dtype=dtype, usecols=range(3)) # 透视表,将电影ID转换为列名称,转换成为一个User-Movie的评分矩阵 ratings_matrix = ratings.pivot_table(index=["userId"], columns=["movieId"],values="rating") #计算用户之间相似度 user_similar = ratings_matrix.T.corr()
-
预测用户对物品的评分 (以用户1对电影1评分为例)
-
# 1. 找出uid用户的相似用户 similar_users = user_similar[1].drop([1]).dropna() # 相似用户筛选规则:正相关的用户 similar_users = similar_users.where(similar_users>0).dropna() # 2. 从用户1的近邻相似用户中筛选出对物品1有评分记录的近邻用户 ids = set(ratings_matrix[1].dropna().index)&set(similar_users.index) finally_similar_users = similar_users.ix[list(ids)] # 3. 结合uid用户与其近邻用户的相似度预测uid用户对iid物品的评分 numerator = 0 # 评分预测公式的分子部分的值 denominator = 0 # 评分预测公式的分母部分的值 for sim_uid, similarity in finally_similar_users.iteritems(): # 近邻用户的评分数据 sim_user_rated_movies = ratings_matrix.ix[sim_uid].dropna() # 近邻用户对iid物品的评分 sim_user_rating_for_item = sim_user_rated_movies[1] # 计算分子的值 numerator += similarity * sim_user_rating_for_item # 计算分母的值 denominator += similarity # 4 计算预测的评分值 predict_rating = numerator/denominator print("预测出用户<%d>对电影<%d>的评分:%0.2f" % (1, 1, predict_rating))
-
封装成方法 预测任意用户对任意电影的评分
def predict(uid, iid, ratings_matrix, user_similar): ''' 预测给定用户对给定物品的评分值 :param uid: 用户ID :param iid: 物品ID :param ratings_matrix: 用户-物品评分矩阵 :param user_similar: 用户两两相似度矩阵 :return: 预测的评分值 ''' print("开始预测用户<%d>对电影<%d>的评分..."%(uid, iid)) # 1. 找出uid用户的相似用户 similar_users = user_similar[uid].drop([uid]).dropna() # 相似用户筛选规则:正相关的用户 similar_users = similar_users.where(similar_users>0).dropna() if similar_users.empty is True: raise Exception("用户<%d>没有相似的用户" % uid) # 2. 从uid用户的近邻相似用户中筛选出对iid物品有评分记录的近邻用户 ids = set(ratings_matrix[iid].dropna().index)&set(similar_users.index) finally_similar_users = similar_users.ix[list(ids)] # 3. 结合uid用户与其近邻用户的相似度预测uid用户对iid物品的评分 numerator = 0 # 评分预测公式的分子部分的值 denominator = 0 # 评分预测公式的分母部分的值 for sim_uid, similarity in finally_similar_users.iteritems(): # 近邻用户的评分数据 sim_user_rated_movies = ratings_matrix.ix[sim_uid].dropna() # 近邻用户对iid物品的评分 sim_user_rating_for_item = sim_user_rated_movies[iid] # 计算分子的值 numerator += similarity * sim_user_rating_for_item # 计算分母的值 denominator += similarity # 计算预测的评分值并返回 predict_rating = numerator/denominator print("预测出用户<%d>对电影<%d>的评分:%0.2f" % (uid, iid, predict_rating)) return round(predict_rating, 2)
-
为某一用户预测所有电影评分
def predict_all(uid, ratings_matrix, user_similar): ''' 预测全部评分 :param uid: 用户id :param ratings_matrix: 用户-物品打分矩阵 :param user_similar: 用户两两间的相似度 :return: 生成器,逐个返回预测评分 ''' # 准备要预测的物品的id列表 item_ids = ratings_matrix.columns # 逐个预测 for iid in item_ids: try: rating = predict(uid, iid, ratings_matrix, user_similar) except Exception as e: print(e) else: yield uid, iid, rating if __name__ == '__main__': for i in predict_all(1, ratings_matrix, user_similar): pass
-
根据评分为指定用户推荐topN个电影
def top_k_rs_result(k): results = predict_all(1, ratings_matrix, user_similar) return sorted(results, key=lambda x: x[2], reverse=True)[:k] if __name__ == '__main__': from pprint import pprint result = top_k_rs_result(20) pprint(result)
2 Item-Based CF 预测电影评分
-
加载ratings.csv,转换为用户-电影评分矩阵并计算用户之间相似度
import os import pandas as pd import numpy as np DATA_PATH = "./datasets/ml-latest-small/ratings.csv" dtype = {"userId": np.int32, "movieId": np.int32, "rating": np.float32} # 加载数据,我们只用前三列数据,分别是用户ID,电影ID,已经用户对电影的对应评分 ratings = pd.read_csv(data_path, dtype=dtype, usecols=range(3)) # 透视表,将电影ID转换为列名称,转换成为一个User-Movie的评分矩阵 ratings_matrix = ratings.pivot_table(index=["userId"], columns=["movieId"],values="rating") #计算用户之间相似度 item_similar = ratings_matrix.corr()
-
预测用户对物品的评分 (以用户1对电影1评分为例)
-
# 1. 找出iid物品的相似物品 similar_items = item_similar[1].drop([1]).dropna() # 相似物品筛选规则:正相关的物品 similar_items = similar_items.where(similar_items>0).dropna() # 2. 从iid物品的近邻相似物品中筛选出uid用户评分过的物品 ids = set(ratings_matrix.ix[1].dropna().index)&set(similar_items.index) finally_similar_items = similar_items.ix[list(ids)] # 3. 结合iid物品与其相似物品的相似度和uid用户对其相似物品的评分,预测uid对iid的评分 numerator = 0 # 评分预测公式的分子部分的值 denominator = 0 # 评分预测公式的分母部分的值 for sim_iid, similarity in finally_similar_items.iteritems(): # 近邻物品的评分数据 sim_item_rated_movies = ratings_matrix[sim_iid].dropna() # 1用户对相似物品物品的评分 sim_item_rating_from_user = sim_item_rated_movies[1] # 计算分子的值 numerator += similarity * sim_item_rating_from_user # 计算分母的值 denominator += similarity predict_rating = numerator/denominator print("预测出用户<%d>对电影<%d>的评分:%0.2f" % (1, 1, predict_rating))
-
封装成方法 预测任意用户对任意电影的评分
def predict(uid, iid, ratings_matrix, user_similar): ''' 预测给定用户对给定物品的评分值 :param uid: 用户ID :param iid: 物品ID :param ratings_matrix: 用户-物品评分矩阵 :param user_similar: 用户两两相似度矩阵 :return: 预测的评分值 ''' print("开始预测用户<%d>对电影<%d>的评分..."%(uid, iid)) # 1. 找出uid用户的相似用户 similar_users = user_similar[uid].drop([uid]).dropna() # 相似用户筛选规则:正相关的用户 similar_users = similar_users.where(similar_users>0).dropna() if similar_users.empty is True: raise Exception("用户<%d>没有相似的用户" % uid) # 2. 从uid用户的近邻相似用户中筛选出对iid物品有评分记录的近邻用户 ids = set(ratings_matrix[iid].dropna().index)&set(similar_users.index) finally_similar_users = similar_users.ix[list(ids)] # 3. 结合uid用户与其近邻用户的相似度预测uid用户对iid物品的评分 numerator = 0 # 评分预测公式的分子部分的值 denominator = 0 # 评分预测公式的分母部分的值 for sim_uid, similarity in finally_similar_users.iteritems(): # 近邻用户的评分数据 sim_user_rated_movies = ratings_matrix.ix[sim_uid].dropna() # 近邻用户对iid物品的评分 sim_user_rating_for_item = sim_user_rated_movies[iid] # 计算分子的值 numerator += similarity * sim_user_rating_for_item # 计算分母的值 denominator += similarity # 计算预测的评分值并返回 predict_rating = numerator/denominator print("预测出用户<%d>对电影<%d>的评分:%0.2f" % (uid, iid, predict_rating)) return round(predict_rating, 2)
-
为某一用户预测所有电影评分
def predict_all(uid, ratings_matrix, item_similar): ''' 预测全部评分 :param uid: 用户id :param ratings_matrix: 用户-物品打分矩阵 :param item_similar: 物品两两间的相似度 :return: 生成器,逐个返回预测评分 ''' # 准备要预测的物品的id列表 item_ids = ratings_matrix.columns # 逐个预测 for iid in item_ids: try: rating = predict(uid, iid, ratings_matrix, item_similar) except Exception as e: print(e) else: yield uid, iid, rating if __name__ == '__main__': for i in predict_all(1, ratings_matrix, item_similar): pass
-
根据评分为指定用户推荐topN个电影
def top_k_rs_result(k): results = predict_all(1, ratings_matrix, item_similar) return sorted(results, key=lambda x: x[2], reverse=True)[:k] if __name__ == '__main__': from pprint import pprint result = top_k_rs_result(20) pprint(result)
import os
import pandas as pd
import numpy as np
#---------------------------加载ratings.csv,转换为用户-电影评分矩阵并计算用户之间相似度--------------------------#
data_path = "./ratings.csv"
dtype = {"userId": np.int32, "movieId": np.int32, "rating": np.float32}
# 加载数据,我们只用前三列数据,分别是用户ID,电影ID,已经用户对电影的对应评分
#range(3):代表遍历的值为0,1,2
ratings = pd.read_csv(data_path, dtype=dtype, usecols=range(3))
"""
index=["userId"]:行名/行索引 为userId,即“用户users-物品items”中的用户users。
columns=["movieId"]:列名/列索引 为movieId,即“用户items-物品items”中的物品items。
values="rating":用户对电影的评分,即用户对物品的评分
"""
# 透视表,将电影ID转换为列名称,转换成为一个User-Movie的评分矩阵
ratings_matrix = ratings.pivot_table(index=["userId"], columns=["movieId"],values="rating")
# print("ratings_matrix",ratings_matrix)
# movieId 1 2 3 4 ... 193583 193585 193587 193609
# userId ...
# 1 4.0 NaN 4.0 NaN ... NaN NaN NaN NaN
# 2 NaN NaN NaN NaN ... NaN NaN NaN NaN
# 3 NaN NaN NaN NaN ... NaN NaN NaN NaN
# 4 NaN NaN NaN NaN ... NaN NaN NaN NaN
# 5 4.0 NaN NaN NaN ... NaN NaN NaN NaN
# ... ... ... ... ... ... ... ... ... ...
# 606 2.5 NaN NaN NaN ... NaN NaN NaN NaN
# 607 4.0 NaN NaN NaN ... NaN NaN NaN NaN
# 608 2.5 2.0 2.0 NaN ... NaN NaN NaN NaN
# 609 3.0 NaN NaN NaN ... NaN NaN NaN NaN
# 610 5.0 NaN NaN NaN ... NaN NaN NaN NaN
# [610 rows x 9724 columns]
"""
计算皮尔逊相关系数
DataFrame中的corr() 计算皮尔逊相关系数,默认是按列进行计算。
假如数据集设置的为index=users 和 columns=items,分别代表users为行索引(行名),items为列索引(列名)。
1.计算用户之间的相似度
因为index=users代表users为行索引(行名),即行索引(行名)为用户,因为corr()默认是按列进行计算的,
那么还需要把行索引(行名)转置为列索引(列名),那么才能计算用户之间的相似度。
user_similar = df.T.corr() 即可 计算用户之间的相似度。
2.计算物品之间的相似度
因为columns=items代表items为列索引(列名),即列索引(列名)便为物品,那么corr()默认是按列进行计算便是计算的是物品之间的相似度。
item_similar = df.corr() 即可 计算物品之间的相似度。
"""
#计算用户之间相似度
user_similar = ratings_matrix.T.corr() #corr() 计算皮尔逊相关系数,默认是按列进行计算。
# print("user_similar",user_similar)
# userId 1 2 3 ... 608 609 610
# userId ...
# 1 1.000000 NaN 0.079819 ... 0.268070 -0.175412 -0.032086
# 2 NaN 1.000000 NaN ... -0.125000 NaN 0.623288
# 3 0.079819 NaN 1.000000 ... -0.395092 NaN 0.569562
# 4 0.207983 NaN NaN ... -0.170501 -0.277350 -0.043786
# 5 0.268749 NaN NaN ... -0.020546 0.384111 0.040582
# ... ... ... ... ... ... ... ...
# 606 0.066378 0.583333 -0.791334 ... 0.240842 0.533002 0.389185
# 607 0.174557 NaN -0.333333 ... 0.200814 0.190117 0.106605
# 608 0.268070 -0.125000 -0.395092 ... 1.000000 0.488929 0.147606
# 609 -0.175412 NaN NaN ... 0.488929 1.000000 -0.521773
# 610 -0.032086 0.623288 0.569562 ... 0.147606 -0.521773 1.000000
# [610 rows x 610 columns]
#---------------------------预测用户对物品的评分 (以用户1对电影1评分为例)--------------------------#
"""
1.DataFrame.loc[i].drop([i])
取出每一行/每一列数据,然后因为DataFrame是用户之间的相似度矩阵,因此drop([i])即为删除自身
(即删除对角线上为1的值,该值代表的是自身和自身的相似度),然后排序数据
使用loc只能指定行列索引的名字去获取:loc -- 先行后列, 是需要通过索引的字符串进行获取。
loc[i]:i可以为行索引,取出每一行;i可以为列索引,取出每一列。
2.DataFrame[1].drop([1]).dropna()
获取DataFrame中列索引为1的这一列数据,然后因为DataFrame是用户之间的相似度矩阵,因此drop([1])即为删除自身
(即删除对角线上为1的值,该值代表的是自身和自身的相似度)。
dropna(axis='rows') 删除存在缺失值的
pandas删除缺失值,使用dropna的前提是,缺失值的类型必须是np.nan
注:不会修改原数据,需要接受返回值
"""
# 1. 找出uid用户的相似用户
# 取出userId列索引为1的这一列数据,即用户1的近邻相似用户
similar_users = user_similar[1].drop([1]).dropna()
# print(similar_users)
# userId
# 3 0.079819
# 4 0.207983
# 5 0.268749
# 6 -0.291636
# 7 -0.118773
# ...
# 606 0.066378
# 607 0.174557
# 608 0.268070
# 609 -0.175412
# 610 -0.032086
# Name: 1, Length: 568, dtype: float64
# 相似用户筛选规则:正相关的用户
# 只取正相关的相似度值:只保留下正值的相似度,丢弃负值的相似度,即用户uid的近邻相似用户并不包含负数值相似度的用户
similar_users = similar_users.where(similar_users>0).dropna()
# print(similar_users)
# userId
# 3 0.079819
# 4 0.207983
# 5 0.268749
# 8 0.469668
# 9 0.918559
# ...
# 600 0.253649
# 601 0.091574
# 606 0.066378
# 607 0.174557
# 608 0.268070
# Name: 1, Length: 377, dtype: float64
"""
set(ratings_matrix[1].dropna().index):对物品movieId为1的有评分的用户
ratings_matrix[1]:即取出该DataFrame中列索引为1的这一列数据。
index:userId行索引值,即最终取出所有的userId行索引值。
set(similar_users.index):用户1的近邻相似用户(不包含负数值的相似度的用户)
similar_users中的index为userId行索引值,即取出所有的userId行索引值。
set(ratings_matrix[1].dropna().index) & set(similar_users.index)
对物品movieId为1的有评分的用户 和 同时与用户1近邻相似(相似度为正值)的用户 进行&相交操作,
即从用户1的近邻相似用户中筛选出对物品1有评分记录的近邻用户。
&:表示交集。
之所以两者取交集,是因为 对物品movieId为1的有评分的用户 不一定和 用户1近邻相似(即不一定相似度为正值),
而 和用户1近邻相似(相似度为正值)的用户 又不一定 对物品movieId为1的有评分,因此两者要进行交集,
"""
# 2. 从用户1的近邻相似用户中筛选出对物品1有评分记录的近邻用户
ids = set(ratings_matrix[1].dropna().index) & set(similar_users.index)
# print("ratings_matrix[1].dropna()",ratings_matrix[1].dropna())
# userId
# 1 4.0
# 5 4.0
# 7 4.5
# 15 2.5
# 17 4.5
# ...
# 606 2.5
# 607 4.0
# 608 2.5
# 609 3.0
# 610 5.0
# Name: 1, Length: 215, dtype: float32
# print("set(ratings_matrix[1].dropna().index)",set(ratings_matrix[1].dropna().index)) #对物品movieId为1的有评分的用户
# print("set(similar_users.index)",set(similar_users.index)) #用户1的近邻相似用户(不包含负数值的相似度的用户)
# print("ids",ids) #对物品movieId为1的有评分的用户 和 用户1的近邻相似用户相交,即从用户1的近邻相似用户中筛选出对物品1有评分记录的近邻用户
"""
警告:utureWarning:
.ix is deprecated. Please use
.loc for label based indexing or
.iloc for positional indexing
分析:
1.直接使用行列索引(先列后行):直接索引 -- 先列后行, 是需要通过索引的字符串进行获取
2.使用loc只能指定行列索引的名字去获取:loc -- 先行后列, 是需要通过索引的字符串进行获取
3.使用iloc可以通过索引的下标去获取:iloc -- 先行后列, 是通过下标进行索引
4.使用ix组合索引:ix -- 先行后列, 可以用上面两种方法混合进行索引
-------------------------------------------------------------------------
replace(to_replace=, value=)
to_replace:替换前的值
value:替换后的值
dropna(axis='rows') 删除存在缺失值的
pandas删除缺失值,使用dropna的前提是,缺失值的类型必须是np.nan
注:不会修改原数据,需要接受返回值
"""
"""
list(ids):list封装所有userId行索引
similar_users.loc[list(ids)]:根据“筛选出来的对物品1有评分记录的同时和用户1相似的”userId行索引 在similar_users中取出对应的用户相似度
"""
# finally_similar_users = similar_users.ix[list(ids)]
finally_similar_users = similar_users.loc[list(ids)]
#根据“筛选出来的对物品1有评分记录的同时和用户1相似的”所有userId行索引到similar_users中取出对应的相似度
# print("finally_similar_users",finally_similar_users)
# userId
# 514 0.047894
# 517 0.080181
# 5 0.268749
# 522 0.046613
# 524 0.384955
# ...
# 484 0.509886
# 488 0.332169
# 490 0.166961
# 504 0.342997
# 509 0.028028
# Name: 1, Length: 157, dtype: float64
# 3. 结合uid用户与其近邻用户的相似度预测uid用户对iid物品的评分
numerator = 0 # 评分预测公式的分子部分的值:用户1对应的每个近邻相似用户其对应的相似度similarity * 该近邻相似的用户 对movieId为1的电影的评分值
denominator = 0 # 评分预测公式的分母部分的值:用户1对应的每个近邻相似用户其对应的相似度similarity 的总和
#遍历 用户1相邻的每个用户sim_uid 和 用户1相邻的每个用户对应的相似度similarity
for sim_uid, similarity in finally_similar_users.iteritems():
# 近邻用户的评分数据
# 用户1相邻的每个用户sim_uid 对应所有物品(电影)的 评分值
# sim_user_rated_movies = ratings_matrix.ix[sim_uid].dropna()
sim_user_rated_movies = ratings_matrix.loc[sim_uid].dropna()
# print("sim_user_rated_movies",sim_user_rated_movies)
# 近邻用户对某个物品(电影)的评分
# 用户1相邻的每个用户sim_uid 对movieId为1的电影的评分值
sim_user_rating_for_item = sim_user_rated_movies[1]
# print("sim_user_rating_for_item",sim_user_rating_for_item)
# 计算分子的值:用户1对应的每个近邻相似用户其对应的相似度similarity * 该近邻相似的用户 对movieId为1的电影的评分值
numerator += similarity * sim_user_rating_for_item
# 计算分母的值:用户1对应的每个近邻相似用户其对应的相似度similarity 的总和
denominator += similarity
# 4 计算预测的评分值
predict_rating = numerator / denominator
print("预测出用户<%d>对电影<%d>的评分:%0.2f" % (1, 1, predict_rating)) #3.87
#---------------------------封装成方法 预测任意用户对任意电影的评分--------------------------#
def predict(uid, iid, ratings_matrix, user_similar):
'''
预测给定用户对给定物品的评分值
:param uid: 用户ID
:param iid: 物品ID
:param ratings_matrix: 用户-物品评分矩阵
:param user_similar: 用户两两相似度矩阵
:return: 预测的评分值
'''
# print("开始预测用户<%d>对电影<%d>的评分..."%(uid, iid))
# 1. 找出uid用户的相似用户
# 从用户两两相似度矩阵中 取出userId列索引为uid 的这一列数据,即用户uid的近邻相似用户
similar_users = user_similar[uid].drop([uid]).dropna()
# 相似用户筛选规则:正相关的用户
# 只取正相关的相似度值:只保留下正值的相似度,丢弃负值的相似度,即用户uid的近邻相似用户并不包含负数值相似度的用户
similar_users = similar_users.where(similar_users>0).dropna()
# 如果该用户没有相似用户
if similar_users.empty is True:
raise Exception("用户<%d>没有相似的用户" % uid)
"""
set(ratings_matrix[iid].dropna().index) & set(similar_users.index)
对物品movieId为iid的有评分的用户 和 同时与用户uid近邻相似(相似度为正值)的用户 进行&相交操作,
即从用户uid的近邻相似用户中筛选出对物品iid有评分记录的近邻用户。
&:表示交集。
"""
# 2. 从uid用户的近邻相似用户中筛选出对 iid物品有评分记录的近邻用户
ids = set(ratings_matrix[iid].dropna().index) & set(similar_users.index)
"""
list(ids):list封装所有userId行索引
similar_users.loc[list(ids)]:根据“筛选出来的对物品iid有评分记录的同时和用户uid相似的”userId行索引 在similar_users中取出对应的用户相似度
"""
# finally_similar_users = similar_users.ix[list(ids)]
finally_similar_users = similar_users.loc[list(ids)]
# 3. 结合uid用户与其近邻用户的相似度预测uid用户对iid物品的评分
numerator = 0 # 评分预测公式的分子部分的值:用户uid对应的每个近邻相似用户其对应的相似度similarity * 该近邻相似的用户对movieId为iid的电影的评分值
denominator = 0 # 评分预测公式的分母部分的值:用户uid对应的每个近邻相似用户其对应的相似度similarity 的总和
for sim_uid, similarity in finally_similar_users.iteritems():
# 近邻用户的评分数据
# 用户uid相邻的每个用户sim_uid 对应所有物品(电影)的 评分值
# sim_user_rated_movies = ratings_matrix.ix[sim_uid].dropna()
sim_user_rated_movies = ratings_matrix.loc[sim_uid].dropna()
# 近邻用户对iid物品的评分
# 用户uid相邻的每个用户sim_uid 对movieId为iid的电影的评分值
sim_user_rating_for_item = sim_user_rated_movies[iid]
# 计算分子的值:用户uid对应的每个近邻相似用户其对应的相似度similarity * 该近邻相似的用户对movieId为iid的电影的评分值
numerator += similarity * sim_user_rating_for_item
# 计算分母的值:用户uid对应的每个近邻相似用户其对应的相似度similarity 的总和
denominator += similarity
# 计算预测的评分值并返回
predict_rating = numerator / denominator
print("预测出用户<%d>对电影<%d>的评分:%0.2f" % (uid, iid, predict_rating))
return round(predict_rating, 2)
#---------------------------为某一用户预测所有电影评分--------------------------#
def predict_all(uid, ratings_matrix, user_similar):
'''
预测全部评分
:param uid: 用户id
:param ratings_matrix: 用户-物品打分矩阵
:param user_similar: 用户两两间的相似度
:return: 生成器,逐个返回预测评分
'''
# 准备要预测的物品的id列表:即所有的物品(电影)
item_ids = ratings_matrix.columns
# 逐个预测:遍历每个物品(电影)
for iid in item_ids:
try:
# 预测某一用户对任意电影的评分
rating = predict(uid, iid, ratings_matrix, user_similar)
except Exception as e:
print(e)
else:
yield uid, iid, rating
#---------------------------根据评分为指定用户推荐topN个电影--------------------------#
def top_k_rs_result(k):
# 为某一用户预测所有电影评分
results = predict_all(1, ratings_matrix, user_similar)
"""
key=lambda x: x[2]
因为yield返回的为uid、iid、rating,x[2]代表rating,表示按照rating评分进行排序
reverse=True:降序
[:k]:表示TopN,即取前K个
"""
# 根据评分为指定用户推荐topN个电影
return sorted(results, key=lambda x: x[2], reverse=True)[:k]
if __name__ == '__main__':
#为某一用户预测所有电影评分
# for i in predict_all(1, ratings_matrix, user_similar):
# pass
#根据评分为指定用户推荐topN个电影
from pprint import pprint
result = top_k_rs_result(20)
pprint(result)
import os
import pandas as pd
import numpy as np
#----------------------加载ratings.csv,转换为用户-电影评分矩阵并计算用户之间相似度---------------------#
data_path = "./ratings.csv"
dtype = {"userId": np.int32, "movieId": np.int32, "rating": np.float32}
# 加载数据,我们只用前三列数据,分别是用户ID,电影ID,已经用户对电影的对应评分
ratings = pd.read_csv(data_path, dtype=dtype, usecols=range(3))
# 透视表,将电影ID转换为列名称,转换成为一个User-Movie的评分矩阵
ratings_matrix = ratings.pivot_table(index=["userId"], columns=["movieId"],values="rating")
#计算用户之间相似度
item_similar = ratings_matrix.corr()
#----------------------预测用户对物品的评分 (以用户1对电影1评分为例)---------------------#
# 1. 找出iid物品的相似物品
similar_items = item_similar[1].drop([1]).dropna()
# 相似物品筛选规则:正相关的物品
similar_items = similar_items.where(similar_items>0).dropna()
# 2. 从iid物品的近邻相似物品中筛选出uid用户评分过的物品
# ids = set(ratings_matrix.ix[1].dropna().index)&set(similar_items.index)
ids = set(ratings_matrix.loc[1].dropna().index)&set(similar_items.index)
# finally_similar_items = similar_items.ix[list(ids)]
finally_similar_items = similar_items.loc[list(ids)]
# 3. 结合iid物品与其相似物品的相似度和uid用户对其相似物品的评分,预测uid对iid的评分
numerator = 0 # 评分预测公式的分子部分的值
denominator = 0 # 评分预测公式的分母部分的值
for sim_iid, similarity in finally_similar_items.iteritems():
# 近邻物品的评分数据
sim_item_rated_movies = ratings_matrix[sim_iid].dropna()
# 1用户对相似物品物品的评分
sim_item_rating_from_user = sim_item_rated_movies[1]
# 计算分子的值
numerator += similarity * sim_item_rating_from_user
# 计算分母的值
denominator += similarity
# 计算预测的评分值并返回
predict_rating = numerator/denominator
print("预测出用户<%d>对电影<%d>的评分:%0.2f" % (1, 1, predict_rating))
#----------------------封装成方法 预测任意用户对任意电影的评分---------------------#
def predict(uid, iid, ratings_matrix, user_similar):
'''
预测给定用户对给定物品的评分值
:param uid: 用户ID
:param iid: 物品ID
:param ratings_matrix: 用户-物品评分矩阵
:param user_similar: 用户两两相似度矩阵
:return: 预测的评分值
'''
print("开始预测用户<%d>对电影<%d>的评分..."%(uid, iid))
# 1. 找出uid用户的相似用户
similar_users = user_similar[uid].drop([uid]).dropna()
# 相似用户筛选规则:正相关的用户
similar_users = similar_users.where(similar_users>0).dropna()
if similar_users.empty is True:
raise Exception("用户<%d>没有相似的用户" % uid)
# 2. 从uid用户的近邻相似用户中筛选出对iid物品有评分记录的近邻用户
ids = set(ratings_matrix[iid].dropna().index)&set(similar_users.index)
# finally_similar_users = similar_users.ix[list(ids)]
finally_similar_users = similar_users.loc[list(ids)]
# 3. 结合uid用户与其近邻用户的相似度预测uid用户对iid物品的评分
numerator = 0 # 评分预测公式的分子部分的值
denominator = 0 # 评分预测公式的分母部分的值
for sim_uid, similarity in finally_similar_users.iteritems():
# 近邻用户的评分数据
# sim_user_rated_movies = ratings_matrix.ix[sim_uid].dropna()
sim_user_rated_movies = ratings_matrix.loc[sim_uid].dropna()
# 近邻用户对iid物品的评分
sim_user_rating_for_item = sim_user_rated_movies[iid]
# 计算分子的值
numerator += similarity * sim_user_rating_for_item
# 计算分母的值
denominator += similarity
# 计算预测的评分值并返回
predict_rating = numerator/denominator
print("预测出用户<%d>对电影<%d>的评分:%0.2f" % (uid, iid, predict_rating))
return round(predict_rating, 2)
#----------------------为某一用户预测所有电影评分---------------------#
def predict_all(uid, ratings_matrix, item_similar):
'''
预测全部评分
:param uid: 用户id
:param ratings_matrix: 用户-物品打分矩阵
:param item_similar: 物品两两间的相似度
:return: 生成器,逐个返回预测评分
'''
# 准备要预测的物品的id列表
item_ids = ratings_matrix.columns
# 逐个预测
for iid in item_ids:
try:
rating = predict(uid, iid, ratings_matrix, item_similar)
except Exception as e:
print(e)
else:
yield uid, iid, rating
#----------------------根据评分为指定用户推荐topN个电影---------------------#
def top_k_rs_result(k):
#为某一用户预测所有电影评分
results = predict_all(1, ratings_matrix, item_similar)
return sorted(results, key=lambda x: x[2], reverse=True)[:k]
if __name__ == '__main__':
for i in predict_all(1, ratings_matrix, item_similar):
pass
#根据评分为指定用户推荐topN个电影
from pprint import pprint
result = top_k_rs_result(20)
pprint(result)