基于协同过滤的电影推荐

日萌社

人工智能AI:Keras PyTorch MXNet TensorFlow PaddlePaddle 深度学习实战(不定时更新)


1.4 案例--基于协同过滤的电影推荐

学习目标

  • 应用基于用户的协同过滤实现电影评分预测
  • 应用基于物品的协同过滤实现电影评分预测

1 User-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")
    #计算用户之间相似度
    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) 

  • 3
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

あずにゃん

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值