基于协同过滤的电影推荐系统

推荐系统

推荐系统是一种信息过滤系统,可以提高搜索结果的质量,并提供搜索项或者与用户的搜索历史相对应的内容。通常运用于预测用户对某项商品的评价或者偏好,国内很多的公司都有使用到,淘宝,京东使用它来向用户推荐商品,爱奇艺,优酷,使用它来推荐下一个要播放的视频,网易云也使用推荐系统提供每日推荐歌曲。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PqjqefPi-1588898733568)(attachment:image.png)]

推荐系统基本上有三种类型:

  • 基于人口统计学
    -主要是依赖于大量的用户数据,该系统背后的基本思想是,更受大众欢迎和好评的电影更有可能被普通观众所喜爱,大家喜欢的就是我喜欢的。
  • 基于内容的过滤
    -根据特定内容建议有相似的内容的电影。该推荐系统使用电影的内容(例如电影的流派,导演,描述,演员等)来提出推荐建议。推荐系统背后的总体思想是,如果某人喜欢某个特定内容,那么他(她)也将喜欢与之相似的内容。
  • 协同过滤
    -协同过滤是利用集体智慧的一个典型方法。生活中如果你想去看电影,一般会问问周围的朋友,看看最近有什么好看的电影推荐,我们一般更倾向于从口味比较类似的朋友那里得到推荐。这就是协同过滤的核心思想。

数据处理

import pandas as pd
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
import matplotlib.pyplot as plt
plt.style.use('seaborn')
#导入数据
d1 = pd.read_csv(r"movies.dat", sep='::', names=['电影ID', '上市年份', '种类'])
d2 = pd.read_csv(r"ratings.dat", sep='::', names=['用户ID', '电影ID', '评分', '时间戳'])
d3 = pd.read_csv(r"users.dat",
                 sep='::',
                 names=['用户ID', '性别', '年龄', '职业', '邮编'])
C:\Users\Administrator\anaconda3\lib\site-packages\ipykernel_launcher.py:2: ParserWarning: Falling back to the 'python' engine because the 'c' engine does not support regex separators (separators > 1 char and different from '\s+' are interpreted as regex); you can avoid this warning by specifying engine='python'.
  
C:\Users\Administrator\anaconda3\lib\site-packages\ipykernel_launcher.py:5: ParserWarning: Falling back to the 'python' engine because the 'c' engine does not support regex separators (separators > 1 char and different from '\s+' are interpreted as regex); you can avoid this warning by specifying engine='python'.
  """
C:\Users\Administrator\anaconda3\lib\site-packages\ipykernel_launcher.py:8: ParserWarning: Falling back to the 'python' engine because the 'c' engine does not support regex separators (separators > 1 char and different from '\s+' are interpreted as regex); you can avoid this warning by specifying engine='python'.
#数据的简单探索
print("行数     : ", d1.shape[0], d2.shape[0], d3.shape[0], sep='\n')
print("列数  : ", d1.shape[1], d2.shape[1], d3.shape[1], sep='\n')
print("\n特征数量 : \n",
      d1.columns.tolist(),
      d2.columns.tolist(),
      d3.columns.tolist(),
      sep='\n')
print("\n缺失值 :  \n",
      d1.isnull().sum(),
      d2.isnull().sum(),
      d3.isnull().sum(),
      sep='\n\n')
print("\n唯一值 :  \n", d1.nunique(), d2.nunique(), d3.nunique(), sep='\n\n')
行数     : 
3883
1000209
6040
列数  : 
3
4
5

特征数量 : 

['电影ID', '上市年份', '种类']
['用户ID', '电影ID', '评分', '时间戳']
['用户ID', '性别', '年龄', '职业', '邮编']

缺失值 :  


电影ID    0
上市年份    0
种类      0
dtype: int64

用户ID    0
电影ID    0
评分      0
时间戳     0
dtype: int64

用户ID    0
性别      0
年龄      0
职业      0
邮编      0
dtype: int64

唯一值 :  


电影ID    3883
上市年份    3883
种类       301
dtype: int64

用户ID      6040
电影ID      3706
评分           5
时间戳     458455
dtype: int64

用户ID    6040
性别         2
年龄         7
职业        21
邮编      3439
dtype: int64

人口统计学数据准备

# 制作一个电影平均评分表
d2_g = d2[['电影ID','评分']].groupby('电影ID').agg(['mean','count']).评分.rename({'mean':'平均评分','count':'评分次数'},axis=1)

协同过滤数据准备

# 数据透视表
d2_piv = d2.pivot_table(index=['用户ID'], columns=['电影ID'], values='评分')

# 填充0,去除列0项
d2_piv_fill = d2_piv.fillna(0)
d2_piv_fill = d2_piv_fill.T
d2_piv_fill = d2_piv_fill.loc[:, (d2_piv_fill != 0).any(axis=0)]

# 标准化
d2_piv_norm = d2_piv_fill.apply(lambda x: (x - np.mean(x)) /
                                (np.max(x) - np.min(x)),
                                axis=1)

# 计算余弦相似度,余弦矩阵可以表现每个用户和项目数值之间的余弦相似度
mov_similarity = cosine_similarity(d2_piv_norm)
user_similarity = cosine_similarity(d2_piv_norm.T)

#放到DataFrame里面
mov_sim_df = pd.DataFrame(mov_similarity,
                          index=d2_piv_norm.index,
                          columns=d2_piv_norm.index)
user_sim_df = pd.DataFrame(user_similarity,
                           index=d2_piv_norm.columns,
                           columns=d2_piv_norm.columns)

基于人口统计学

目的:基于加权评分推荐10部高分电影

  • 我们需要一个指标来给电影评分或评分
  • 计算每部电影的分数
  • 排序分数并向用户推荐收视率最高的电影
  • 我们可以使用电影的平均评分作为得分,但使用它的评分不够合理,因为平均评分很容易出现数量的干扰问题,比如评分为5但是只有2票的电影,不能被认为比平均评分,4.8但是有40票的电影更好。
  • 因此,我使用IMDB的加权评分。

WR = (v ÷ (v+m)) × R + (m ÷ (v+m)) × c

  • WR, 加权得分(weighted rating)
  • R, 该电影的用户投票的平均得分
  • v, 该电影的投票人数
  • m, 要求列出电影的最低投票数(我这里使用第80个百分位)
  • c, 所有电影的平均得分(现在为3.24)
c = d2_g.平均评分.mean()
m = d2_g.评分次数.quantile(0.8)
#筛选电影,743部
p_movies = d2_g.loc[d2_g['评分次数'] >= m]
p_movies.shape
(743, 2)
# 定义WR函数,来确定新的评分
def WR(x, m=m, c=c):
    v = x['评分次数']
    R = x['平均评分']
    return (v / (v + m) * R) + (m / (m + v) * c)
p_movies['加权分数'] = p_movies.apply(WR, axis=1)
C:\Users\Administrator\anaconda3\lib\site-packages\ipykernel_launcher.py:1: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  """Entry point for launching an IPython kernel.
#基于加权评分推荐10部电影
p_movies.sort_values('加权分数', ascending=False).head(10)
平均评分评分次数加权分数
电影ID
3184.55455822274.342050
8584.52496622234.316925
5274.51041723044.310825
2604.45369429914.301311
11984.47772525144.297141
504.51710617834.269206
27624.40626324594.232855
28584.31738634284.197429
5934.35182325784.193044
20284.33735426534.184453

协同过滤

下面我们将使用电影推荐的数据来构建一个推荐系统,实现以下功能

  • 输入电影名生成前10部最相似电影
  • 输入用户名生成前10名最相似用户及其相似度
  • 输入用户名生成相似用户评分最高的电影名列表及其频次
  • 预测用户评分
#定义一个方法返回相似度前k的电影
def top10_movie(movie_ID, k=10):
    count = 1
    print('和电影ID:{}相似的电影有 :\n'.format(movie_ID))
    for i in mov_sim_df.sort_values(by=movie_ID,
                                    ascending=False).index[1:k + 1]:
        print('NO. {}: {}'.format(count, i))
        count += 1
#定义一个方法返回相似度前k的用户
def top5_users(user_ID, k=10):
    if user_ID not in d2_piv_norm.columns:
        return ('没有用户ID: {} 这个用户的数据'.format(user))

    print('最相似的用户有:\n')
    sim_values = user_sim_df.sort_values(
        by=user_ID, ascending=False).loc[:, user_ID].tolist()[1:k + 1]
    sim_users = user_sim_df.sort_values(by=user_ID,
                                        ascending=False).index[1:k + 1]
    zipped = zip(
        sim_users,
        sim_values,
    )
    for user, sim in zipped:
        print('user ID: {0}, 相似度为: {1:.2f}'.format(user, sim))
#定义一个方法返回K个相似用户的w个最高频次电影
def mov_reccomend(user_ID, k=100, w=10):
    if user_ID not in d2_piv_norm.columns:
        return ('没有用户ID: {} 这个用户的数据'.format(user))
    sim_users = user_sim_df.sort_values(by=user_ID,
                                        ascending=False).index[1:k + 1]
    top_mov = []
    mov_recomm = {}
    for i in sim_users:
        top_mov.append(d2_piv_norm[d2_piv_norm.loc[:, i] ==
                                   d2_piv_norm.loc[:, i].max()].index.tolist())
    for i in range(len(top_mov)):
        for j in top_mov[i]:
            if j in mov_recomm:
                mov_recomm[j] += 1
            else:
                mov_recomm[j] = 1
    mov_recomm_list = sorted(mov_recomm.items(),
                             key=lambda item: item[1],
                             reverse=True)
    return mov_recomm_list[0:w]
top10_movie(2)
和电影ID:2相似的电影有 :

NO. 1: 3489
NO. 2: 653
NO. 3: 60
NO. 4: 317
NO. 5: 2161
NO. 6: 2054
NO. 7: 673
NO. 8: 3438
NO. 9: 2193
NO. 10: 367
top5_users(5)
最相似的用户有:

user ID: 1227, 相似度为: 0.26
user ID: 281, 相似度为: 0.26
user ID: 3240, 相似度为: 0.25
user ID: 1484, 相似度为: 0.24
user ID: 590, 相似度为: 0.24
user ID: 1407, 相似度为: 0.24
user ID: 5749, 相似度为: 0.24
user ID: 3538, 相似度为: 0.24
user ID: 5496, 相似度为: 0.24
user ID: 1104, 相似度为: 0.24
mov_reccomend(3)
[(1015, 2),
 (314, 2),
 (290, 2),
 (1257, 2),
 (1959, 2),
 (3671, 1),
 (1246, 1),
 (3006, 1),
 (1661, 1),
 (1017, 1)]

数据源:提取码:8hkb–来自百度网盘超级会员V4的分享)

评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

WiFi下的365

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

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

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

打赏作者

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

抵扣说明:

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

余额充值