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


Posted in Python onMay 31, 2018

本文实例为大家分享了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()

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

Python 相关文章推荐
python简单的函数定义和用法实例
May 07 Python
Python cx_freeze打包工具处理问题思路及解决办法
Feb 13 Python
详解Python中的__new__、__init__、__call__三个特殊方法
Jun 02 Python
Python+matplotlib+numpy实现在不同平面的二维条形图
Jan 02 Python
Python中defaultdict与lambda表达式用法实例小结
Apr 09 Python
Caffe均值文件mean.binaryproto转mean.npy的方法
Jul 09 Python
python 实现在一张图中绘制一个小的子图方法
Jul 07 Python
python中property属性的介绍及其应用详解
Aug 29 Python
Python Tornado批量上传图片并显示功能
Mar 26 Python
Python requests HTTP验证登录实现流程
Nov 05 Python
python 如何用terminal输入参数
May 25 Python
python 爬取天气网卫星图片
Jun 07 Python
python控制windows剪贴板,向剪贴板中写入图片的实例
May 31 #Python
python用户评论标签匹配的解决方法
May 31 #Python
python批量查询、汉字去重处理CSV文件
May 31 #Python
python破解zip加密文件的方法
May 31 #Python
python删除本地夹里重复文件的方法
Nov 19 #Python
Python处理命令行参数模块optpars用法实例分析
May 31 #Python
python筛选出两个文件中重复行的方法
May 31 #Python
You might like
php 远程关机操作的代码
2008/12/05 PHP
php 操作excel文件的方法小结
2009/12/31 PHP
自己写了一个php检测文件编码的函数
2014/04/21 PHP
Yii2中使用join、joinwith多表关联查询
2016/06/30 PHP
一个加密JavaScript的开源工具PACKER2.0.2
2006/11/04 Javascript
window.location和document.location的区别分析
2008/12/23 Javascript
一些经常会用到的Javascript检测函数
2010/05/31 Javascript
javascript获取ckeditor编辑器的值(实现代码)
2013/11/18 Javascript
ff下JQuery无法监听input的keyup事件的解决方法
2013/12/12 Javascript
javascript中声明函数的方法及调用函数的返回值
2014/07/22 Javascript
jQuery+json实现动态创建复杂表格table的方法
2016/10/25 Javascript
详解bootstrap用dropdown-menu实现上下文菜单
2017/09/22 Javascript
记录一次完整的react hooks实践
2019/03/11 Javascript
layui table单元格事件修改值的方法
2019/09/24 Javascript
js实现轮播图效果 纯js实现图片自动切换
2020/08/09 Javascript
使用Pyrex来扩展和加速Python程序的教程
2015/04/13 Python
windows10系统中安装python3.x+scrapy教程
2016/11/08 Python
详解Python中如何写控制台进度条的整理
2018/03/07 Python
基于python实现聊天室程序
2018/07/27 Python
解决Python一行输出不显示的问题
2018/12/03 Python
pymongo中group by的操作方法教程
2019/03/22 Python
PyQt5 实现给窗口设置背景图片的方法
2019/06/13 Python
Django MEDIA的配置及用法详解
2019/07/25 Python
详谈tensorflow gfile文件的用法
2020/02/05 Python
新手入门学习python Numpy基础操作
2020/03/02 Python
Python如何使用正则表达式爬取京东商品信息
2020/06/01 Python
CSS3教程(4):网页边框和网页文字阴影
2009/04/02 HTML / CSS
CSS3 input框的实现代码类似Google登录的动画效果
2020/08/04 HTML / CSS
html5-canvas中使用clip抠出一个区域的示例代码
2018/05/25 HTML / CSS
Feelunique澳大利亚:欧洲的化妆品零售电商
2019/12/18 全球购物
数控专业大学生的自我鉴定
2013/11/13 职场文书
六年级学生期末评语
2014/12/26 职场文书
高三毕业评语
2014/12/31 职场文书
队名及霸气口号大全
2015/12/25 职场文书
漫画「日和酱的要求是绝对的」第3卷封面公开
2022/03/21 日漫
python开发制作好看的时钟效果
2022/05/02 Python