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 随机生成中文验证码的实例代码
Mar 20 Python
Python pickle模块用法实例
Apr 14 Python
opencv python 2D直方图的示例代码
Jul 20 Python
python使用xlsxwriter实现有向无环图到Excel的转换
Dec 12 Python
python如何获取当前文件夹下所有文件名详解
Jan 25 Python
Win10下Python3.7.3安装教程图解
Jul 08 Python
python3获取当前目录的实现方法
Jul 29 Python
Python+OpenCV实现旋转文本校正方式
Jan 09 Python
python实现字符串和数字拼接
Mar 02 Python
python UIAutomator2使用超详细教程
Feb 19 Python
python调试工具Birdseye的使用教程
May 25 Python
Python实现列表拼接和去重的三种方式
Jul 02 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
基于mysql的bbs设计(五)
2006/10/09 PHP
php excel类 phpExcel使用方法介绍
2010/08/21 PHP
Symfony2实现在doctrine中内置数据的方法
2016/02/05 PHP
Ajax中的JSON格式与php传输过程全面解析
2017/11/14 PHP
php微信开发之关注事件
2018/06/14 PHP
javascript中的107个基础知识收集整理 推荐
2010/03/29 Javascript
jquery图片放大功能简单实现
2013/08/01 Javascript
js一维数组、多维数组和对象的混合使用方法
2016/04/03 Javascript
JS瀑布流实现方法实例分析
2016/12/19 Javascript
jQuery实现在新增加的元素上添加事件方法案例分析
2017/02/09 Javascript
详解Vue.use自定义自己的全局组件
2017/06/14 Javascript
获取当前按钮或者html的ID名称实例(推荐)
2017/06/23 Javascript
jQuery代码优化方法总结
2018/01/29 jQuery
使用vue + less 实现简单换肤功能的示例
2018/02/21 Javascript
JS实现移动端触屏拖拽功能
2018/07/31 Javascript
vue项目使用axios发送请求让ajax请求头部携带cookie的方法
2018/09/26 Javascript
详解如何用VUE写一个多用模态框组件模版
2018/09/27 Javascript
vue 使用vue-i18n做全局中英文切换的方法
2018/10/29 Javascript
vue实现将一个数组内的相同数据进行合并
2019/11/07 Javascript
[13:40]TI3青蛙君全程回顾 DOTA2我们为梦想再战
2013/09/13 DOTA
[05:09]2016国际邀请赛中国区预选赛淘汰赛首日精彩回顾
2016/06/29 DOTA
python中类的一些方法分析
2014/09/25 Python
python轮询机制控制led实例
2020/05/03 Python
python接口自动化之ConfigParser配置文件的使用详解
2020/08/03 Python
Python调用系统命令os.system()和os.popen()的实现
2020/12/31 Python
python基于pexpect库自动获取日志信息
2021/02/01 Python
HTML5 transform三维立方体实现360无死角三维旋转效果
2014/08/22 HTML / CSS
amazeui模态框弹出后立马消失并刷新页面
2020/08/19 HTML / CSS
美国户外服装和装备购物网站:Outland USA
2020/03/22 全球购物
幼儿园校车司机的岗位职责
2014/01/30 职场文书
如何写一封打动人心的求职信
2014/02/17 职场文书
大学生社会实践方案
2014/05/11 职场文书
单位承诺书格式
2014/05/21 职场文书
低碳生活的宣传标语
2014/06/23 职场文书
前台岗位职责
2015/02/13 职场文书
财务工作失误检讨书
2015/02/19 职场文书