基本概念
Slope One的基本概念很简单, 例子1, 用户X, Y和A都对Item1打了分. 同时用户X,Y还对Item2打了分, 用户A对Item2可能会打多少分呢?
User | Rating to Item 1 | Rating to Item 2 |
X | 5 | 3 |
Y | 4 | 3 |
A | 4 | ? |
根据SlopeOne算法, 应该是:4 - ((5-3) + (4-3))/2 = 2.5.
解释一下. 用户X对Item1的rating是5, 对Item2的rating是3, 那么他可能认为Item2应该比Item1少两分. 同时用户Y认为Item2应该比Item1少1分. 据此我们知道所有对Item1和Item2都打了分的用户认为Item2会比Item1平均少1.5分. 所以我们有理由推荐用户A可能会对Item2打(4-1.5)=2.5分;
很简单是不是? 找到对Item1和Item2都打过分的用户, 算出rating差的平均值, 这样我们就能推测出对Item1打过分的用户A对Item2的可能Rating, 并据此向A用户推荐新项目.
这里我们能看出Slope One算法的一个很大的优点, 在只有很少的数据时候也能得到一个相对准确的推荐, 这一点可以解决Cold Start的问题.
利用上面的直观,我们定义item i 相对于 item j 的平均偏差:
其中 S j,i () 表示同时对item i 和 j 给予了评分的用户集合,而 card() 表示集合包含的元素数量。
有了上面的定义后,我们可以使用 获得用户 u 对 item j 的预测值。当把所有这种可能的预测平均起来,可以得到:
其中 Rj 表示所有用户 u 已经给予评分且满足条件 ( i≠j 且 S j,i 非空) 的item集合。
加权SlopeOne 算法:
# -*- coding: utf-8 -*-
"""
Created on Thu Apr 16 15:38:58 2015
@author: admin
"""
import os
os.environ['NLS_LANG'] = 'SIMPLIFIED CHINESE_CHINA.UTF8' #必须置于cx之前, 解决Oracle导入中文乱码问题
import cx_Oracle as co
import pandas as pd
class SlopeOne(object):
def __init__(self):
self.diffs = {}
self.freqs = {}
self.userRatings = {}
def sql2dict(self, data):
for line in data:
user=line[0]
item=line[1]
rating=line[3]
if user in self.userRatings:
item_rating=self.userRatings[user] # 将行字典赋予用户
else:
item_rating={}
item_rating[item]=rating
self.userRatings[user]=item_rating
return self.userRatings
def predict(self, userprefs): #recieve the ItemRating from one user,output the recommend ItemRating
preds, freqs = {}, {}
for item, rating in userprefs.iteritems():
for diffitem, diffratings in self.diffs.iteritems():
try:
freq = self.freqs[diffitem][item]
except KeyError:
continue
preds.setdefault(diffitem, 0.0)
freqs.setdefault(diffitem, 0)
preds[diffitem] += freq * (diffratings[item] + rating)
freqs[diffitem] += freq
recommend = dict([(item, value / freqs[item])
for item, value in preds.iteritems()
if item not in userprefs and freqs[item] > 0])
return recommend
def update(self, userdata): # calcu the card and diff
for ratings in userdata.itervalues():
for item1, rating1 in ratings.iteritems():
self.freqs.setdefault(item1, {})
self.diffs.setdefault(item1, {})
for item2, rating2 in ratings.iteritems():
self.freqs[item1].setdefault(item2, 0)
self.diffs[item1].setdefault(item2, 0.0)
self.freqs[item1][item2] += 1
self.diffs[item1][item2] += rating1 - rating2
for item1, ratings in self.diffs.iteritems():
for item2 in ratings:
ratings[item2] /= self.freqs[item1][item2]
if __name__=='__main__':
dsn=co.makedsn('192.168.113.226','11521','boss')
db = co.connect('zsboss','zsboss123',dsn)
cursor=db.cursor()
cursor.execute ("select * from recommendation.live_preference_02 where devno in (select devno from recommendation.user_sample_1000)")
data=cursor.fetchall()
cursor.close()
db.commit()
s=SlopeOne()
userdata=s.sql2dict(data)
s.update(userdata)
recommend_all={}
k=0
for instance in userdata:
recommend_all[instance]=s.predict(userdata[instance])
k+=1
if k%100==0:
print k
'''
oppps!! dict type can be used like this
if __name__ == '__main__':
userdata = dict(
alice=dict(squid=1.0,
cuttlefish=0.5,
octopus=0.2),
bob=dict(squid=1.0,
octopus=0.5,
nautilus=0.2),
carole=dict(squid=0.2,
octopus=1.0,
cuttlefish=0.4,
nautilus=0.4),
dave=dict(cuttlefish=0.9,
octopus=0.4,
nautilus=0.5),
)
s = SlopeOne()
s.update(userdata)
print s.predict(dict(squid=0.4))
#for print
c=0
for i in recommend_all.iteritems():
c+=1
if c == 2 :
break
print i
'''