Python简单电影推荐算法实现
- 具体需求要求
- 源代码
- 运行截图
- 总结
具体需求要求
编写程序,生成数据模拟(也可以使用网上爬取的真实数据)多人对多部定影的打分(1~5分),然后根据这些数据对某用户A进行推荐。
推荐规则为:在已有的数据中选择与该用户A的爱好最相似的用户B,然后从最相似的用户B已看过但用户A还没看过的电影中选择B打分最高的电影推荐给用户A。其中,相似度的计算标准:(1)两个用户共同打分过的电影越多,越相似;(2)两个用户对共同打分的电影的打分越接近,越相似。
要求:
1、随机生成或爬取的数据不少于5个用户,且每个用户看的电影不少于3部;将数据放在excel表格中。
源代码
import functools
import time
import csv
# 自定义排序函数
def compare_personal(x, y):
if x[1][0] != y[1][0]:
return y[1][0] - x[1][0]
else:
return x[1][1] - y[1][1]
class RecommendBasedUser:
def __init__(self, movie, rating, top):
self.movies = movie
self.ratings = rating
self.tops = top
# {'用户ID':[电影ID,电影评分]}
self.userDict = {}
self.moviesDict = {}
self.recommends = []
def formatData(self):
# 数据形式 userId,movieId,rating,timestamp
for rating in self.ratings:
temp = (rating[1], float(rating[2]))
if rating[0] in self.userDict.keys():
self.userDict[rating[0]].append(temp)
else:
self.userDict[rating[0]] = [temp]
for each in self.movies:
self.moviesDict[each[0]] = (each[1], each[2])
def getNearestUser(self, userid):
# 用户A看过的电影和评分
temp = self.userDict[userid]
# 用户A看过的电影和评分构建为字典
moviesRatingsOfUserid = {}
for each in temp:
moviesRatingsOfUserid[each[0]] = each[1]
# 数据形式{(userid,x):[共同打分过的电影数,共同打分的分数接近程度]}x为数据集中的所有的元素
similarityDict = {}
for key, value in self.userDict.items():
# key为用户ID,value为用户看过电影列表
if key != userid:
moviesRatingsOfX = {}
for each in value:
moviesRatingsOfX[each[0]] = each[1]
# 通过对两用户的电影集合求交集获得共同打分电影
common_movies = set(moviesRatingsOfUserid.keys()) & set(moviesRatingsOfX.keys())
commons = len(common_movies)
# 通过计算两用户共同电影打分的绝对值差值作为分数接近的程度(差值越小两者越接近)
difference = 0
for each in common_movies:
difference += abs(moviesRatingsOfUserid[each] - moviesRatingsOfX[each])
similarityDict[(userid, key)] = (commons, difference/(commons+1))
# 对相似度字典进行排序,自定义排序规则为首先比较字典值的第一位元素(降序),若相同再比较第二位元素(升序)
similarity = sorted(similarityDict.items(), key=functools.cmp_to_key(compare_personal))
# print(similarity)
# 获得最相似的用户B
tuple_of_x = similarity[0]
# 用户B看过的电影和评分构建为字典
moviesRatingsOfMaxSim = {}
for each in self.userDict[tuple_of_x[0][1]]:
moviesRatingsOfMaxSim[each[0]] = each[1]
# 获取用户A没有看过的电影
recommendList = list(set(moviesRatingsOfMaxSim.keys()).difference(set(moviesRatingsOfUserid.keys())))
temp_top_n = []
for each in recommendList:
temp_top_n.append((each, moviesRatingsOfMaxSim[each]))
temp_top_n.sort(key=lambda x: x[1], reverse=True)
self.recommends = [temp_top_n[x] for x in range(self.tops)]
print("与用户" + str(userid) + "最相似的用户为" + str(tuple_of_x[0][1]))
print("两者共同打分过的电影数为:", tuple_of_x[1][0])
print("两者共同打分的电影的评分的平均绝对值差为:", tuple_of_x[1][1])
print("推荐电影列表为:")
for each in self.recommends:
temp = self.moviesDict[each[0]]
print("电影名:" + str(temp[0]))
print("电影类型:" + str(temp[1]))
def recommendForUser(self, userid):
self.formatData()
self.getNearestUser(userid)
def readDataSet(filename):
files = open(filename, "r", encoding=None)
read_csv = csv.reader(files, dialect='excel')
data = []
for line in read_csv:
data.append(line)
files.close()
return data
start = time.time()
movies = readDataSet(".//ml-latest-small/movies.csv")
# 数据形式 userId,movieId,rating,timestamp
ratings = readDataSet(".//ml-latest-small/ratings.csv")
demo = RecommendBasedUser(movies, ratings, top=10)
demo.recommendForUser('100')
print("处理的数据为%d条" % (len(demo.ratings)))
end = time.time()
print("运行时间: %f s" % (end - start))
运行截图
总结
基于用户相似度的推荐算法,主要是计算用户之间的相似度,由于此相似度的规则是先比较共同评分电影的数量,再比较评分电影的评分值接近程度,因此,我采用一个设计的字典来记录目标用户与样本集中的用户之间的相似度,将某用户和目标用户ID构成的元组作为字典的键,然后分别计算公共打分电影数和电影评分差距构成的列表作为字典的值,通过自定义的排序函数可以实现按照电影数降序和评分差距升序来对字典构成的列表进行排序。通过修改topN的值可以实现对用户的TOPN推荐。