基于Python的电影推荐系统

news2025/2/24 5:22:17

电影推荐系统
目录
电影推荐系统 1

  1. 数据描述 1
  2. 变量说明 1
  3. 程序介绍 2

本次课程作业在 small-movielens 数据集的基础上,对用户、电影、评分数据进行了处理,然后根据 Pearson 相关系数计算出用户与其他用户之间的相似度,根据相似度进行推荐和预测评分,最后再根据数据集中的测试数据,计算该推荐系统的MAE等。

  1. 数据描述
    数据集来源于 MovieLens|GroupLens 网站。完整的数据集是由 943 个用户对 1682 个项目的 100000 个评分组成。每位用户至少对 20 部电影进行了评分。用户和项从 1 开始连续编号。数据是随机排列的。这是一个以选项卡分隔的:用户id|项id|分级|时间戳 列表。从 1970年1月1日 UTC 开始,时间戳是unix时间戳。在这些数据集的基础上,small-movielens 还包括 u1-u5,ua,ub 等七组测试集 (.base)/训练集 (.test) 数据。其中,u1-u5 是以 8:2 的比例随机生成互斥的训练集和测试集;ua、ub 是按照 9:1 的比例随机生成的互斥的训练集和测试集,不同的是,这两组数据的测试集中每个用户是对正好 10 部不同电影进行了评分。数据集如下图所示。
    在这里插入图片描述

  2. 变量说明
    users :保存所有用户,不重复 list 类型 存储结构:[user1, user2,…]
    userWatchedMovie :保存所有用户看过的所有电影,字典嵌套字典类型 存储结构:{user1:{movie1:rating1, movie2:rating2, …}, user2:{…},…}
    movieUser :保存用户与用户之间共同看过的电影,字典嵌套字典嵌套 list 存储结构:{user1:{user2:[movie1,…],user3:[…],…},user2:{user3:[…],…},…}
    userSimilarity :保存用户与用户之间的相似度(皮尔逊相似度) 存储结构:{user1:{user2:sim, user3:sim,…}, user2:{user3:sim, …}, …}
    allUserTopNSim :保存每个用户都取前 n=10 个最相似的用户,以及相似度 存储结构:{user1:{user01:sim,user02:sim,…},user2:{user01:sim,…},…}
    recommendedMovies :从最相似的用户中推荐,每个相似用户推荐两部,同时计算出预测值并保存在这个变量里 存储结构:{user1:{user01:{movie01:predictionRating,…},user02:[…],…},user2:{user01:[…],…},…}
    usersTest :测试集文件中的所有用户 存储结构:同 users
    userWatchedMovieTest :测试集文件中所有用户看过的所有电影 存储结构:同 userWatchedMovie
    movieAlsoInTest :保存推荐的电影正好也在用户测试数据中看过的那一些电影,以便后面进行 MAE 计算 存储结构:{user1:[movie1,movie2,…],…}
    averageRating :保存每个用户对被推荐的电影的预测平均分 存储结构:{user1:{movie01:[count,sumPreRating,averageRating],…},…}
    eachUserMAE :保存对每个用户而言计算出的 MAE 存储结构:{user1:MAE,user2:MAE,…}

# -*- coding:utf-8 -*-
from math import sqrt
import os

TOPN = 10  # 设置每个用户相似度最高的前TOPN个用户
AMOUNT = 5  # 设置前TOPN个相似用户中每个用户给当前用户推荐的电影数量

# 读取训练集数据文件
filePath = './data/u1.base'
users = []  # 保存所有用户,不重复list类型
userWatchedMovie = {}  # 保存所有用户看过的所有电影,字典嵌套字典类型
movieUser = {}  # 保存用户与用户之间共同看过的电影,字典嵌套字典嵌套list
with open(filePath, 'r') as trainFile:
    # 计算所有的用户以及用户看过的电影和评分,保存到相关字典变量中
    for line in trainFile:
        # type(line)是string类型,其中分离出来的userId等也都是string类型
        # strip()方法是去除指定首尾字符
        (userId, movieId, rating, timestamp) = line.strip('\n').split('\t')
        if userId not in users:
            users.append(userId)
            userWatchedMovie.setdefault(userId, {})
        userWatchedMovie[userId][movieId] = float(rating)


# 计算用户与用户共同看过的电影
for x in range(len(users)-1):
    movieUser.setdefault(users[x], {})
    for y in range(x+1, len(users)):
        movieUser[users[x]].setdefault(users[y], [])
        for m in userWatchedMovie[users[x]].keys():
            if m in userWatchedMovie[users[y]].keys():
                movieUser[users[x]][users[y]].append(m)

# 计算用户与用户之间的相似度,皮尔逊相似度
userSimilarity = {}
for a in movieUser.keys():
    userSimilarity.setdefault(a, {})
    for b in movieUser[a].keys():
        userSimilarity[a].setdefault(b, 0)
        if len(movieUser[a][b]) == 0:
            userSimilarity[a][b] = 0  # 如果两个人没有看过同一部电影,则相似度为0
            continue
        else:
            # 下面开始进行相似度的计算,皮尔森相关系数
            avgUserA = 0  # A用户打分的平均值
            avgUserB = 0  # B用户打分的平均值
            numerator = 0  # Pearson的分子部分
            denominatorA = 0  # Pearson的分母A部分
            denominatorB = 0  # Pearson的分母B部分
            count = len(movieUser[a][b])  # 保存两个用户共同看过的电影的数量
            # 这里用到了一个召回率的因素,因为在前述的实验过程中,发现最相似的用户
            # 相似度达到了 1.0 ,感觉有点不正常,比如说用户1和用户9,共同只看过两部电影,但是相似度却等于 1.0
            # 这个是不太正确的,所以加入了召回率的考量
            # 加入之后,发现用户1和用户9的相似度从1.0下降到0.19,感觉是比较合理的
            factor = 0
            if count > 20:  # 20是自己指定的
                factor = 1.0
            else:
                if count < 0:
                    factor = 0
                else:
                    factor = (-0.0025 * count * count) + (0.1 * count)
            for movie in movieUser[a][b]:  # 对于两个用户都看过的每一部电影,进行计算
                avgUserA += float(userWatchedMovie[a][movie])
                avgUserB += float(userWatchedMovie[b][movie])
            avgUserA = float(avgUserA / count)
            avgUserB = float(avgUserB / count)
            # print(avgUserA)
            # print(avgUserB)
            for m in movieUser[a][b]:
                # print(userWatchedMovie[a][m])
                tempA = float(userWatchedMovie[a][m]) - avgUserA
                tempB = float(userWatchedMovie[b][m]) - avgUserB
                # print(tempA)
                numerator += tempA * tempB
                denominatorA += pow(tempA, 2) * 1.0
                denominatorB += pow(tempB, 2) * 1.0
            # print(numerate)
            if denominatorA != 0 and denominatorB != 0:
                userSimilarity[a][b] = factor * (numerator / (sqrt(denominatorA * denominatorB)))
            else:
                userSimilarity[a][b] = 0


# 每个用户都取前n个最相似的用户,以便后续进行推荐
# singleUserTopNSim = {}
allUserTopNSim = {}

for currentUserId in users:  # 计算当前用户的最相似的前n个用户
    singleUserSim = {}  # 存放单个用户对所有用户的相似度
    allUserTopNSim.setdefault(currentUserId, {})  # 存放所有用户的TopN相似的用户
    for compareUserId in users:
        if currentUserId == compareUserId:
            break
        else:
            singleUserSim[compareUserId] = userSimilarity[compareUserId][currentUserId]
    if int(currentUserId) != len(users):
        singleUserSim.update(userSimilarity[currentUserId])
    # print(currentUserId, end=' ')
    # print(singleUserSim)
    # python中的字典是无序的,但是有时候会根据value值来取得字典中前n个值,
    # 此处思想是将字典转化成list,经过排序,取得前n个值,再将list转化回字典
    # 进行排序,取前N个相似的用户,此时singleSortedSim是一个list类型:[(key,value),(key,value),(key,value),...]
    singleSortedSim = sorted(singleUserSim.items(), key=lambda item: item[1], reverse=True)
    singleTopN = singleSortedSim[:TOPN]  # 取出前N个最相似的值
    for single in singleTopN:
        allUserTopNSim[currentUserId][single[0]] = single[1]  # 保存当前用户计算出的TopN个相似用户以及相似度


# 从最相似的用户中推荐,每个相似用户推荐number部,那么每个用户就能得到推荐的number*10部电影
recommendedMovies = {}
for oneUser in allUserTopNSim.keys():
    recommendedMovies.setdefault(oneUser, {})
    for simUser in allUserTopNSim[oneUser].keys():
        oneMovieList = []
        simUserMovieList = []
        number = 0
        recommendedMovies[oneUser].setdefault(simUser, {})
        for movie in userWatchedMovie[simUser].keys():
            if number >= AMOUNT:  # 每个人推荐数量为number部的电影数
                break
            if movie not in userWatchedMovie[oneUser].keys():  # and (movie not in recommendedMovies[oneUser]):
                # 计算预测权值
                if int(oneUser) < int(simUser):
                    # oneMovieList.append(list(movieUser[oneUser][simUser]))
                    # simUserMovieList.append(list(movieUser[oneUser][simUser]))
                    length = len(movieUser[oneUser][simUser])
                    #simUserMovieList.append(movie)
                    #tupleOne = tuple(oneMovieList)
                    #tupleSim = tuple(simUserMovieList)
                    sumOne = 0.0
                    sumSim = 0.0
                    for i in movieUser[oneUser][simUser]:
                        sumOne += userWatchedMovie[oneUser].get(i)
                        sumSim += userWatchedMovie[simUser].get(i)
                    sumSim += userWatchedMovie[simUser].get(movie)
                    avgOneUser = sumOne / length
                    avgSimUser = sumSim / (length + 1)
                    predictionRating = avgOneUser + (userWatchedMovie[simUser][movie] - avgSimUser)
                    recommendedMovies[oneUser][simUser][movie] = predictionRating  #这是一个需要计算的值
                    number += 1
                else:
                    # oneMovieList.append(movieUser[simUser][oneUser])
                    # simUserMovieList.append(movieUser[simUser][oneUser])
                    length = len(movieUser[simUser][oneUser])
                    # simUserMovieList.append(movie)
                    sumOne = 0
                    sumSim = 0
                    for i in movieUser[simUser][oneUser]:
                        sumOne += userWatchedMovie[oneUser].get(i)
                        sumSim += userWatchedMovie[simUser].get(i)
                    sumSim += userWatchedMovie[simUser].get(movie)
                    avgOneUser = sumOne / length
                    avgSimUser = sumSim / (length + 1)
                    predictionRating = avgOneUser + (userWatchedMovie[simUser][movie] - avgSimUser)
                    recommendedMovies[oneUser][simUser][movie] = predictionRating  #这是一个需要计算的值
                    number += 1
            else:
                continue
'''
# 读取测试集数据文件
filePathTest = './data/u1.test'
usersTest = []  # 保存所有用户,不重复list类型
userWatchedMovieTest = {}  # 保存所有用户看过的所有电影,字典嵌套字典类型
with open(filePathTest, 'r') as testFile:
    # 计算所有的用户以及用户看过的电影和评分,保存到相关字典变量中
    for lineTest in testFile:
        (userIdTest, movieIdTest, ratingTest, timestampTest) = lineTest.strip('\n').split('\t')
        if userIdTest not in usersTest:
            usersTest.append(userIdTest)
            userWatchedMovieTest.setdefault(userIdTest, {})
        userWatchedMovieTest[userIdTest][movieIdTest] = ratingTest
'''
'''
# 要找到在测训练集中用户没有看过,但是在测试集中用户看过并且被推荐过的电影,这样后面好计算MAE(平均绝对误差)
movieRecommendedAlsoInTest = {}
for user in usersTest:
    movieRecommendedAlsoInTest.setdefault(user, [])
    for m in recommendedMovies[user]:
        if m in userWatchedMovieTest[user].keys():
            movieRecommendedAlsoInTest[user].append(m)
'''

'''
writeFilePath = './data/movieuser.txt'
if os.path.exists(writeFilePath):
    os.remove(writeFilePath)
writeFile = open(writeFilePath, 'w')
for m in movieUser.keys():
    for n in movieUser[m].keys():
        writeFile.writelines([m, '\t', n, '\t', str(movieUser[m][n])])
        writeFile.write('\n')
writeFile.close()
'''

writeFilePath = './data/userWatchedMovie.txt'
if os.path.exists(writeFilePath):
    os.remove(writeFilePath)
writeFile = open(writeFilePath, 'w')
for m in userWatchedMovie.keys():
    for n in userWatchedMovie[m].keys():
        writeFile.writelines([m, '\t', n, '\t', str(userWatchedMovie[m][n])])
        writeFile.write('\n')
writeFile.close()

writeFilePath = './data/allUserTop10Sim.txt'
if os.path.exists(writeFilePath):
    os.remove(writeFilePath)
writeFile = open(writeFilePath, 'w')
for m in allUserTopNSim.keys():
    for n in allUserTopNSim[m].keys():
        writeFile.writelines([m, '\t', n, '\t', str(allUserTopNSim[m][n])])
        writeFile.write('\n')
writeFile.close()


writeFilePath = './data/recoMovieWithRating.txt'
if os.path.exists(writeFilePath):
    os.remove(writeFilePath)
writeFile = open(writeFilePath, 'w')
for m in recommendedMovies.keys():
    for n in recommendedMovies[m].keys():
        writeFile.writelines([m, '\t', n, '\t', str(recommendedMovies[m][n])])
        writeFile.write('\n')
writeFile.close()


writeFilePath = './data/userSimilarity.txt'
if os.path.exists(writeFilePath):
    os.remove(writeFilePath)
writeFile = open(writeFilePath, 'w')
for m in userSimilarity.keys():
    for n in userSimilarity[m].keys():
        writeFile.writelines([m, '\t', n, '\t', str(userSimilarity[m][n])])
        writeFile.write('\n')
writeFile.close()

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2765.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

用Python来表白,把情书写进她的照片里

前言 这不已经十一月了&#xff0c;22年马上就过完了&#xff0c;各位兄弟有对象了吗&#xff0c;现在就是缺钱还缺对象 退一步来说&#xff0c;有心仪的人吗啊&#xff0c;如果有的话&#xff0c;看看这篇 程序员的表白小妙招吧 实现步骤 想要实现把情书写在像素中&#xf…

【Web-HTML基础】颜色赋值、背景图片、文本和字体相关样式、元素的显示方式display、盒子模型、部分标签自带外边距或内边距

目录 颜色赋值 背景图片 文本和字体相关样式 元素的显示方式display 盒子模型 盒子模型之content内容 盒子模型之margin外边距 盒子模型之border边框 盒子模型之padding内边距 部分标签自带外边距或内边距 综合代码实现 颜色赋值 三原色: 红绿蓝 RGB RedGreenBlue …

【2022秋招面经】——深度学习

文章目录请写出常用的损失函数&#xff0c;平方损失、交叉熵损失、softmax损失函数和hinge1. 0-1 损失函数2. 绝对值损失函数3. 平方损失函数4. log 对数损失函数5. 指数损失函数&#xff08;exponential loss&#xff09;6. Hinge 损失函数7. 感知损失(preceptron loss)函数8.…

网课查题接口系统使用教程

网课查题接口系统使用教程 本平台优点&#xff1a;免费查题接口搭建 多题库查题、独立后台、响应速度快、全网平台可查、功能最全&#xff01; 1.想要给自己的公众号获得查题接口&#xff0c;只需要两步&#xff01; 2.题库&#xff1a;题库后台http://daili.jueguangzhe.cn/…

使用微服务实现多设备自动调度

在实现app自动化过程中&#xff0c;有时候我们需要将多个手机中的任务&#xff0c;按照某种规则进行调度&#xff0c;这时仅仅使用移动端的脚本就无法完成了&#xff0c;需要配合使用微服务来完成。下面就举个例子来实际说明如何使用。 需求 假设我们有100台手机做任务&#…

计算机毕业设计(附源码)python疫情下的学生出入管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

云IDE产品介绍

前言 CSDN开发云官网发布了最新产品【云IDE】产品&#xff01;【云IDE】将提供一键秒级构建云开发环境&#xff0c;是专为开发者打造的一款低代码开发产品&#xff0c;云端预制了常见的开发环境&#xff0c;无需下载安装&#xff0c;一键创建项目&#xff0c;灵活配置代码仓和云…

【数据库05】玩转SQL的高阶特性

前 言 &#x1f349; 作者简介&#xff1a;半旧518&#xff0c;长跑型选手&#xff0c;立志坚持写10年博客&#xff0c;专注于java后端 ☕专栏简介&#xff1a;相当硬核&#xff0c;黑皮书《数据库系统概念》读书笔记&#xff0c;讲解&#xff1a; 1.数据库系统的基本概念(数据…

22.10.31补卡 22CCPC桂林C题

Problem - C - Codeforces 这题题解是请教了学长之后才做出来的, 若是想看题解请看http://t.csdn.cn/unAyg 本篇文章只作为做题记录 写了一天半...感觉自己是不太适合写区域赛的题了, 还是多学学算法和数论好了 ---------------------------------------------------------…

学习笔记-NTLM中继

NTLM中继 免责声明 本文档仅供学习和研究使用,请勿使用文中的技术源码用于非法用途,任何人造成的任何负面影响,与本人无关. NTLM hash 分为 NTLMv1 NTLMv2 NTLMv2 session 三种&#xff0c;NTLMv2 的强度比 NTLMv1 强了不少 &#xff0c;我们在实战中&#xff0c;如果获得的是 …

【Android Studio学习】第一篇、制作一个拥有登录和注册功能的简易APP

目录 第一部分、前言 1、目标效果 2、准备知识 第二部分、详细步骤 1、新建Empty工程 ​2、添加资源文件 3、搭建注册界面 4、搭建登录界面 5、编写注册界面和登录界面的代码 6、设置APP初始界面 7、连接手机&#xff0c;编译工程 第三部分、总结 1、参考资料 2、…

基于反馈技术的宽带低噪声放大器的设计

低噪声放大器是通信、雷达、电子对抗及遥控遥测系统中的必不可少的重要部件&#xff0c;它位于射频接收系统的前端&#xff0c;主要功能是对天线接收到的微弱射频信号进行线性放大&#xff0c;同时抑制各种噪声干扰&#xff0c;提高系统的灵敏度。特别是随着通信、电子对抗、微…

基于Java+Vue+uniapp微信小程序实现餐厅校园订餐平台

博主介绍&#xff1a;✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取联系&#x1f345;精彩专栏推荐订阅&#x1f447;&#x1f…

springboot二手车交易系统

功能介绍 目 录 第一章 绪论 5 1.1研究背景 5 1.2平台现状分析 5 1.3系统实现的功能 5 1.4二手车交易管理信息系统的特点 6 1.5本文的组织结构 6 第二章开发技术与环境配置 7 2.1 SpringBoot框架 7 2.2Java语言简介 7 2.3 MySQL环境配置 8 2.4 MyEclipse环境配置 8 2.5 mysql数…

黑马程序员C++类和对象【5】 —— 运算符重载(蓝桥杯必备知识)万字超详解

目录 &#x1f921;加号运算符重载 &#x1f921;左移运算符重载 &#x1f921;递增运算符重载 &#x1f921;递减运算符重载 &#x1f921;赋值运算符重载 &#x1f921;关系运算符重载 &#x1f921;函数调用运算符重载&#xff08;仿函数&#xff09; &#x1f921;加…

牛客竞赛每日俩题 - 动态规划1

目录 DP入门&#xff08;存储之前状态以简化&#xff09; DP解决最短路问题 DP入门&#xff08;存储之前状态以简化&#xff09; 拆分词句_牛客题霸_牛客网 思路&#xff1a; 方法&#xff1a;动态规划 状态&#xff1a; 子状态&#xff1a;前1&#xff0c;2&#xff0c;3&a…

蓝桥杯国奖一等奖,经历回顾

文章目录1. 自我介绍2. 参赛经历3. 我的一些经验4. 结语本篇内容为一位粉丝的投稿内容&#xff0c;希望对大家有所帮助。 1. 自我介绍 我是来自一所普通双非的大三学生&#xff0c;以下为主要成绩: 2022蓝桥杯Java组&#xff0c;全国一等奖2022高教社杯全国大学生数学建模竞赛…

windows一键部署java项目

前言 前段时间老大看我很闲...然后给我下达了一项很重要的任务&#xff1a;windows一键部署&#xff0c;需要把服务&#xff08;jdk、tomcat、mysql、jar包、前端压缩包&#xff09;打成一个exe应用程序&#xff0c;点击安装会启动所有服务&#xff0c;打开浏览器http://localh…

Java 程序设计报告[对接java的迭代器接口]

1&#xff1a;程序的功能设计与分析 -&#xff1a;将实现deque与stack -&#xff1a;采用继承与内部类来提高程序的拓展性、安全性、简洁性 -&#xff1a;对接到java.util.iterator中的iterator接口与iterable接口 2&#xff1a;程序的特点分析 -&#xff1a;观察到队列、栈都…

二战华为成功上岸,准备了小半年,要个27k应该也算不上很高吧~

先说下我基本情况&#xff0c;本科不是计算机专业&#xff0c;现在是学通信&#xff0c;然后做图像处理&#xff0c;可能面试官看我不是科班出身没有问太多计算机相关的问题&#xff0c;因为第一次找工作&#xff0c;华为的游戏专场又是最早开始的&#xff0c;就投递了&#xf…