欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

python3实现基于用户的协同过滤

程序员文章站 2023-01-05 14:19:23
本文实例为大家分享了python3实现基于用户协同过滤的具体代码,供大家参考,具体内容如下 废话不多说,直接看代码。 #!/usr/bin/python3...

本文实例为大家分享了python3实现基于用户协同过滤的具体代码,供大家参考,具体内容如下

废话不多说,直接看代码。

#!/usr/bin/python3 
# -*- coding: utf-8 -*- 
#20170916号协同过滤电影推荐基稿 
#字典等格式数据处理及直接写入文件 
 
 
##from numpy import * 
import time 
from math import sqrt 
##from texttable import texttable 
 
 
class cf: 
 
 def __init__(self, movies, ratings, k=5, n=20): 
  self.movies = movies#[movieid,title,genres] 
  (self.train_data,self.test_data) = (ratings[0], ratings[1])#[userid::movieid::rating::timestamp] 
  # 邻居个数 
  self.k = k 
  # 推荐个数 
  self.n = n 
  # 用户对电影的评分 
  # 数据格式{'userid用户id':[(movieid电影id,rating用户对电影的评星)]} 
  self.userdict = {} 
  # 对某电影评分的用户 
  # 数据格式:{'movieid电影id':[userid,用户id]} 
  # {'1',[1,2,3..],...} 
  self.itemuser = {} 
  # 邻居的信息 
  self.neighbors = [] 
  # 推荐列表 
  self.recommandlist = []#包含dist和电影id 
  self.recommand = [] #训练集合测试集的交集,且仅有电影id 
  #用户评过电影信息 
  self.train_user = [] 
  self.test_user = [] 
  #给用户的推荐列表,仅含movieid 
  self.train_rec =[] 
  self.test_rec = [] 
  #test中的电影评分预测数据集合, 
  self.forecast = {}#前k个近邻的评分集合 
  self.score = {}#最终加权平均后的评分集合{“电影id”:预测评分} 
  #召回率和准确率 
  self.pre = [0.0,0.0] 
  self.z = [0.0, 0.0] 
 ''''' 
 userdict数据格式: 
 '3': [('3421', 0.8), ('1641', 0.4), ('648', 0.6), ('1394', 0.8), ('3534', 0.6), ('104', 0.8), 
 ('2735', 0.8), ('1210', 0.8), ('1431', 0.6), ('3868', 0.6), ('1079', 1.0), ('2997', 0.6), 
 ('1615', 1.0), ('1291', 0.8), ('1259', 1.0), ('653', 0.8), ('2167', 1.0), ('1580', 0.6), 
 ('3619', 0.4), ('260', 1.0), ('2858', 0.8), ('3114', 0.6), ('1049', 0.8), ('1261', 0.2), 
 ('552', 0.8), ('480', 0.8), ('1265', 0.4), ('1266', 1.0), ('733', 1.0), ('1196', 0.8), 
 ('590', 0.8), ('2355', 1.0), ('1197', 1.0), ('1198', 1.0), ('1378', 1.0), ('593', 0.6), 
 ('1379', 0.8), ('3552', 1.0), ('1304', 1.0), ('1270', 0.6), ('2470', 0.8), ('3168', 0.8), 
 ('2617', 0.4), ('1961', 0.8), ('3671', 1.0), ('2006', 0.8), ('2871', 0.8), ('2115', 0.8), 
 ('1968', 0.8), ('1136', 1.0), ('2081', 0.8)]} 
 itemuser数据格式: 
 {'42': ['8'], '2746': ['10'], '2797': ['1'], '2987': ['5'], '1653': ['5', '8', '9'], 
 '194': ['5'], '3500': ['8', '10'], '3753': ['6', '7'], '1610': ['2', '5', '7'], 
 '1022': ['1', '10'], '1244': ['2'], '25': ['8', '9'] 
 ''' 
  
# 将ratings转换为userdict和itemuser 
 def formatrate(self,train_or_test): 
  self.userdict = {} 
  self.itemuser = {} 
  for i in train_or_test:#[userid,movieid,rating,timestamp] 
   # 评分最高为5 除以5 进行数据归一化 
##   temp = (i[1], float(i[2]) / 5) 
   temp = (i[1], float(i[2])) 
##   temp = (i[1], i[2]) 
   # 计算userdict {'用户id':[(电影id,评分),(2,5)...],'2':[...]...}一个观众对每一部电影的评分集合 
   if(i[0] in self.userdict): 
    self.userdict[i[0]].append(temp) 
   else: 
    self.userdict[i[0]] = [temp] 
   # 计算itemuser {'电影id',[用户id..],...}同一部电影的观众集合 
   if(i[1] in self.itemuser): 
    self.itemuser[i[1]].append(i[0]) 
   else: 
    self.itemuser[i[1]] = [i[0]]   
 
 # 格式化userdict数据 
 def formatuserdict(self, userid, p):#userid为待查询目标,p为近邻对象 
  user = {} 
  #user数据格式为:电影id:[userid的评分,近邻用户的评分] 
  for i in self.userdict[userid]:#i为userdict数据中的每个括号同81行 
   user[i[0]] = [i[1], 0] 
  for j in self.userdict[p]: 
   if(j[0] not in user): 
    user[j[0]] = [0, j[1]]#说明目标用户和近邻用户没有同时对一部电影评分 
   else: 
    user[j[0]][1] = j[1]#说明两者对同一部电影都有评分 
  return user 
  
   
 
 # 计算余弦距离 
 def getcost(self, userid, p): 
  # 获取用户userid和p评分电影的并集 
  # {'电影id':[userid的评分,p的评分]} 没有评分为0 
  user = self.formatuserdict(userid, p) 
  x = 0.0 
  y = 0.0 
  z = 0.0 
  for k, v in user.items():#k是键,v是值 
   x += float(v[0]) * float(v[0]) 
   y += float(v[1]) * float(v[1]) 
   z += float(v[0]) * float(v[1]) 
  if(z == 0.0): 
   return 0 
  return z / sqrt(x * y) 
 #计算皮尔逊相似度 
##  def getcost(self, userid, p): 
##   # 获取用户userid和l评分电影的并集 
##   # {'电影id':[userid的评分,l的评分]} 没有评分为0 
##   user = self.formatuserdict(userid, p) 
##   sumxsq = 0.0 
##   sumysq = 0.0 
##   sumxy = 0.0 
##   sumx = 0.0 
##   sumy = 0.0 
##   n = len(user) 
##   for k, v in user.items(): 
##    sumx +=float(v[0]) 
##    sumy +=float(v[1]) 
##    sumxsq += float(v[0]) * float(v[0]) 
##    sumysq += float(v[1]) * float(v[1]) 
##    sumxy += float(v[0]) * float(v[1]) 
##   up = sumxy -sumx*sumy/n 
##   down = sqrt((sumxsq - pow(sumxsq,2)/n)*(sumysq - pow(sumysq,2)/n)) 
##   if(down == 0.0): 
##    return 0 
##   return up/down 
 
# 找到某用户的相邻用户 
 def getnearestneighbor(self, userid): 
  neighbors = [] 
  self.neighbors = [] 
  # 获取userid评分的电影都有那些用户也评过分 
  for i in self.userdict[userid]:#i为userdict数据中的每个括号同95行#user数据格式为:电影id:[userid的评分,近邻用户的评分] 
   for j in self.itemuser[i[0]]:#i[0]为电影编号,j为看同一部电影的每位用户 
    if(j != userid and j not in neighbors): 
     neighbors.append(j) 
  # 计算这些用户与userid的相似度并排序 
  for i in neighbors:#i为用户id 
   dist = self.getcost(userid, i) 
   self.neighbors.append([dist, i]) 
  # 排序默认是升序,reverse=true表示降序 
  self.neighbors.sort(reverse=true) 
  self.neighbors = self.neighbors[:self.k]#切片操作,取前k个 
##  print('neighbors',len(neighbors)) 
 
  # 获取推荐列表 
 def getrecommandlist(self, userid): 
  self.recommandlist = [] 
  # 建立推荐字典 
  recommanddict = {} 
  for neighbor in self.neighbors:#这里的neighbor数据格式为[[dist,用户id],[],....] 
   movies = self.userdict[neighbor[1]]#movies数据格式为[(电影id,评分),(),。。。。] 
   for movie in movies: 
    if(movie[0] in recommanddict): 
     recommanddict[movie[0]] += neighbor[0]####???? 
    else: 
     recommanddict[movie[0]] = neighbor[0] 
 
  # 建立推荐列表 
  for key in recommanddict:#recommanddict数据格式{电影id:累计dist,。。。} 
   self.recommandlist.append([recommanddict[key], key])#recommandlist数据格式【【累计dist,电影id】,【】,。。。。】 
  self.recommandlist.sort(reverse=true) 
##  print(len(self.recommandlist)) 
  self.recommandlist = self.recommandlist[:self.n] 
##  print(len(self.recommandlist)) 
 # 推荐的准确率 
 def getprecision(self, userid): 
##  print("开始!!!") 
#先运算test_data,这样最终self.neighbors等保留的是后来计算train_data后的数据(不交换位置的话就得在gr函数中增加参数保留各自的neighbor) 
  (self.test_user,self.test_rec) = self.getrecommand(self.test_data,userid)#测试集的用户userid所评价的电影和给该用户推荐的电影列表 
  (self.train_user,self.train_rec) = self.getrecommand(self.train_data,userid)#训练集的用户userid所评价的所有电影集合(self.train_user)和给该用户推荐的电影列表(self.train_rec) 
#西安电大的张海朋:基于协同过滤的电影推荐系统的构建(2015)中的准确率召回率计算 
  for i in self.test_rec: 
   if i in self.train_rec: 
    self.recommand.append(i) 
  self.pre[0] = len(self.recommand)/len(self.train_rec) 
  self.z[0] = len(self.recommand)/len(self.test_rec) 
  #北京交大黄宇:基于协同过滤的推荐系统设计与实现(2015)中的准、召计算 
  self.recommand = []#这里没有归零的话,下面计算初始recommand不为空 
  for i in self.train_rec: 
   if i in self.test_user: 
    self.recommand.append(i) 
  self.pre[1] = len(self.recommand)/len(self.train_rec) 
  self.z[1] = len(self.recommand)/len(self.test_user) 
##  print(self.train_rec,self.test_rec,"20",len(self.train_rec),len(self.train_rec)) 
  #对同一用户分别通过训练集和测试集处理 
 def getrecommand(self,train_or_test,userid): 
  self.formatrate(train_or_test) 
  self.getnearestneighbor(userid) 
  self.getrecommandlist(userid) 
  user = [i[0] for i in self.userdict[userid]]#用户userid评分的所有电影集合 
  recommand = [i[1] for i in self.recommandlist]#推荐列表仅有电影id的集合,区别于recommandlist(还含有dist) 
##  print("userid该用户已通过训练集测试集处理") 
  return (user,recommand) 
 #对test的电影进行评分预测 
 def forecast(self): 
  self.forecast = {}#?????前面变量统一定义初始化后,函数内部是否需要该初始化???? 
  same_movie_id = [] 
  neighbors_id = [i[1] for i in self.neighbors] #近邻用户数据仅含用户id的集合  
     
  for i in self.test_user:#i为电影id,即在test里的i有被推荐到 
   if i in self.train_rec: 
    same_movie_id.append(i) 
    for j in self.itemuser[i]:#j为用户id,即寻找近邻用户的评分和相似度 
     if j in neighbors_id: 
      user = [i[0] for i in self.userdict[j]]#self.userdict[userid]数据格式:数据格式为[(电影id,评分),(),。。。。];这里的userid应为近邻用户p 
      a = self.neighbors[neighbors_id.index(j)]#找到该近邻用户的数据【dist,用户id】 
      b = self.userdict[j][user.index(i)]#找到该近邻用户的数据【电影id,用户id】 
      c = [a[0], b[1], a[1]] 
      if (i in self.forecast): 
       self.forecast[i].append(c) 
      else: 
       self.forecast[i] = [c]#数据格式:字典{“电影id”:【dist,评分,用户id】【】}{'589': [[0.22655856915174025, 0.6, '419'], [0.36264561173211646, 1.0, '1349']。。。} 
##  print(same_movie_id) 
  #每个近邻用户的评分加权平均计算得预测评分 
  self.score = {} 
  if same_movie_id :#在test里的电影是否有在推荐列表里,如果为空不做判断,下面的处理会报错 
   for movieid in same_movie_id: 
    total_d = 0 
    total_down = 0 
    for d in self.forecast[movieid]:#此时的d已经是最里层的列表了【】;self.forecast[movieid]的数据格式[[]] 
     total_d += d[0]*d[1] 
     total_down += d[0] 
    self.score[movieid] = [round(total_d/total_down,3)]#加权平均后取3位小数的精度 
   #在test里但是推荐没有的电影id,这里先按零计算 
   for i in self.test_user: 
    if i not in movieid: 
     self.score[i] = [0] 
  else: 
   for i in self.test_user: 
    self.score[i] = [0] 
##  return self.score 
 #计算平均绝对误差mae 
 def cal_mae(self,userid): 
  self.formatrate(self.test_data) 
##  print(self.userdict) 
  for item in self.userdict[userid]: 
   if item[0] in self.score: 
    self.score[item[0]].append(item[1])#self.score数据格式[[预测分,实际分]] 
##  #过渡代码 
##  for i in self.score: 
##   pass 
  return self.score 
    # 基于用户的推荐 
 # 根据对电影的评分计算用户之间的相似度 
## def recommendbyuser(self, userid): 
##  print("亲,请稍等片刻,系统正在快马加鞭为你运作中")   #人机交互辅助解读, 
##  self.getprecision(self,userid) 
 
 
# 获取数据 
def readfile(filename): 
 files = open(filename, "r", encoding = "utf-8") 
 data = [] 
 for line in files.readlines(): 
  item = line.strip().split("::") 
  data.append(item) 
 return data 
 files.close() 
def load_dict_from_file(filepath): 
 _dict = {} 
 try: 
  with open(filepath, 'r',encoding = "utf -8") as dict_file: 
   for line in dict_file.readlines(): 
    (key, value) = line.strip().split(':') 
    _dict[key] = value 
 except ioerror as ioerr: 
  print ("文件 %s 不存在" % (filepath)) 
 return _dict 
def save_dict_to_file(_dict, filepath): 
 try: 
  with open(filepath, 'w',encoding = "utf - 8") as dict_file: 
   for (key,value) in _dict.items(): 
    dict_file.write('%s:%s\n' % (key, value)) 
 
 except ioerror as ioerr: 
  print ("文件 %s 无法创建" % (filepath)) 
def writefile(data,filename): 
 with open(filename, 'w', encoding = "utf-8")as f: 
  f.write(data) 
 
 
# -------------------------开始------------------------------- 
 
def start3(): 
 start1 = time.clock() 
 movies = readfile("d:/d/movies.dat") 
 ratings = [readfile("d:/d/201709train.txt"),readfile("d:/d/201709test.txt")] 
 demo = cf(movies, ratings, k=20) 
 userid = '1000' 
 demo.getprecision(userid) 
## print(demo.forecast()) 
 demo.forecast() 
 print(demo.cal_mae(userid)) 
## demo.recommendbyuser(id)  #上一句只能实现固定用户查询,这句可以实现“想查哪个查哪个”,后期可以加个循环,挨个查,查到你不想查 
 print("处理的数据为%d条" % (len(ratings[0])+len(ratings[1]))) 
## print("____---",len(ratings[0]),len(ratings[1])) 
## print("准确率: %.2f %%" % (demo.pre * 100)) 
## print("召回率: %.2f %%" % (demo.z * 100)) 
 print(demo.pre) 
 print(demo.z) 
 end1 = time.clock() 
 print("耗费时间: %f s" % (end1 - start1)) 
def start1(): 
 start1 = time.clock() 
 movies = readfile("d:/d/movies.dat") 
 ratings = [readfile("d:/d/201709train.txt"),readfile("d:/d/201709test.txt")] 
 demo = cf(movies, ratings, k = 20) 
 demo.formatrate(ratings[0]) 
 writefile(str(demo.userdict),"d:/d/dd/userdict.txt") 
 writefile(str(demo.itemuser), "d:/d/dd/itemuser.txt") 
## save_dict_to_file(demo.userdict,"d:/d/dd/userdict.txt") 
## save_dict_to_file(demo.itemuser,"d:/d/dd/itemuser.txt") 
 print("处理结束") 
## with open("d:/d/dd/userdict.txt",'r',encoding = 'utf-8') as f: 
##  diction = f.read() 
##  i = 0 
##  for j in eval(diction): 
##   print(j) 
##   i += 1 
##   if i == 4: 
##    break 
def start2(): 
 start1 = time.clock() 
 movies = readfile("d:/d/movies.dat") 
 ratings = [readfile("d:/d/201709train.txt"),readfile("d:/d/201709test.txt")] 
 demo = cf(movies, ratings, k = 20) 
 demo.formatrate_tomovie(ratings[0]) 
 writefile(str(demo.moviedict),"d:/d/dd/moviedict.txt") 
## writefile(str(demo.userdict),"d:/d/dd/userdict.txt") 
## writefile(str(demo.itemuser), "d:/d/dd/itemuser.txt") 
## save_dict_to_file(demo.userdict,"d:/d/dd/userdict.txt") 
## save_dict_to_file(demo.itemuser,"d:/d/dd/itemuser.txt") 
 print("处理结束")  
 
if __name__ == '__main__': 
 start1() 

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。