赞
踩
python -- 面向程序员的数据挖掘指南-推荐系统入门-004
import pandas as pd
import numpy as np
还有一种比较流行的基于物品的协同过滤算法,名为Slope One,它最大的优势是简单,因此易于实现。
Slope One算法是在一篇名为《Slope One:基于在线评分系统的协同过滤算法》的论文中提出的,由Lemire和Machlachlan合著。这篇论文非常值得一读。
我们用一个简单的例子来了解这个算法。假设Amy给PSY打了3分,Whitney Houston打了4分;Ben给PSY打了4分。我们要预测Ben会给Whitney Houston打几分。 用表格来描述这个问题即:
我们可以用以下逻辑来预测Ben对Whitney Houston的评分:由于Amy给Whitney Houston打的分数要比PSY的高一分,所以我们预测Ben也会给高一分,即给到5分。
这就是Slope One算法:Slope One算法是基于不同物品之间的评分差的线性算法,预测用户对物品评分的个性化算法。
你可以将Slope One分为两个步骤:
Step1:计算物品之间的评分差的均值,记为物品间的评分偏差(两物品同时被评分);
Step2:根据物品间的评分偏差和用户的历史评分,预测用户对未评分的物品的评分。
下面用简单的例子来说明一下Slope One算法。
第一步、我们来考察PSY和Taylor Swift之间的差值, 有两个用户(Amy和Ben)同时对PSY和Taylor Swift打过分。
# 计算差值, 一共有2个用户对两者都进行了打分
a = (4-3)+(5-2)
# 计算平均值
b = a/2
print(a, b)
4 2.0
所以PSY和Taylor Swift的差异是2,即用户们给Taylor Swift的评分比PSY要平均高出两分。
试想我们的音乐站点有100万个用户对20万个歌手做评价。如果有一个新用户对10个歌手做了评价,我们是否需要重新计算20万×20万的差异数据,或是有其他更简单的方法?
答案是当然不啦,只需要在差异数据的基础上更新即可,完全没必要重新计算一遍差异数据。
举个例子,说明一下:
比如说Taylor Swift和PSY的差值是2,假设是根据9位用户的评价计算得到的。当有一个新用户对Taylor Swift打了5分,PSY打了1分时,更新后的差值为:
# 计算差异总值,再求平均值
print('差值为',(9*2+4)/(9+1))
差值为 2.2
第二步:使用加权的Slope One算法进行预测
好,现在我们有了物品之间的差异值,下面就用它来进行预测。这里我们将使用加权的Slope One算法来进行预测, 计算出Ben对Whitney Houston的预测评分
1、Ben的打分的有['Taylor Swift', 'PSY'];
2、计算Whitney Houston 和 Taylor Swift的差值:Amy: 4-4=0, Daisy: 3-5=-2, 即差值均 值为 -1;
3、Ben对Taylor Swift 打5分,根据Whitney Houston 和 Taylor Swift的差值,计算Ben对Whitney Houston的预测评分: 5-1=4;
4、计算Whitney Houston 和 PSY 的差值:Amy: 4-3=1, Clara: 4-3.5=0.5, 即差值均值为 (1+0.5)/2=0.75;
5、Ben对 PSY 打2分,根据Whitney Houston 和 PSY 的差值,计算Ben对Whitney Houston的预测评分: 2+0.75=2.75;
6、计算加权分值:(4*2+2.75*2)/(2+2)=3.375。
users = {"Amy": {"Taylor Swift": 4, "PSY": 3, "Whitney Houston": 4},
"Ben": {"Taylor Swift": 5, "PSY": 2},
"Clara": {"PSY": 3.5, "Whitney Houston": 4},
"Daisy": {"Taylor Swift": 5, "Whitney Houston": 3}}
df = pd.DataFrame(users)
print(df)
Amy Ben Clara Daisy
Taylor Swift 4 5.0 NaN 5.0
PSY 3 2.0 3.5 NaN
Whitney Houston 4 NaN 4.0 3.0
1、计算差值矩阵
# 计算差值矩阵
def computeDeviations(data):
result = {}
for b1 in data.iterrows():
result[b1[0]] = {}
for b2 in data.iterrows():
temp = (b1[1]-b2[1]).dropna()
result[b1[0]][b2[0]] = temp.mean()
result = pd.DataFrame(result)
return result
print(computeDeviations(df))
####################################
Taylor Swift PSY Whitney Houston
Taylor Swift 0.0 -2.00 -1.00
PSY 2.0 0.00 0.75
Whitney Houston 1.0 -0.75 0.00
2、预测加权分数
# 预测加权分数
def predictRating(user, data):
result = computeDeviations(data)
# 获取用户需要预测的项目
pre_user = list(data[user][data[user].isna()].index)
for index in pre_user:
#计算分值
score = (result[index]+data[user]).dropna()
#计算加权系数
x = {}
for item in score.index:
x[item] = len(data.reindex([index,item]).dropna(axis=1).index)
x = pd.Series(x)
print((score*x).sum()/x.sum())
predictRating('Ben', df)
3.375
老规矩,编写成一个推荐类
class recommender:
def __init__(self, data):
self.data = data
self.recomment = []
# 计算差值矩阵
def computeDeviations(self):
result = {}
for b1 in self.data.iterrows():
result[b1[0]] = (b1[1]-self.data).mean(axis=1)
self.deviations = pd.DataFrame(result)
# 预测加权分数
def predictRating(self, user):
# 获取用户需要预测的项目
pre_user = list(self.data[user][self.data[user].isna()].index)
for index in pre_user:
#计算分值
score = (self.deviations[index]+self.data[user]).dropna()
#计算加权系数
x = {}
for item in score.index:
x[item] = len(self.data.reindex([index,item]).dropna(axis=1).index)
x = pd.Series(x)
self.recomment.append([(score*x).sum()/x.sum(), index])
下面我们为Clara推荐:
r = recommender(df)
r.computeDeviations()
r.predictRating('Clara')
print(r.recomment)
[[5.25, 'Taylor Swift']]
Slope One算法适用于物品更新不频繁,数量相对较稳定并且物品数目明显小于用户数的场景。依赖用户的用户行为日志和物品偏好的相关内容。
优点:
1.算法简单,易于实现,执行效率高;
2.可以发现用户潜在的兴趣爱好;
缺点:
依赖用户行为,存在冷启动问题和稀疏性问题。
MovieLens数据集
让我们在另一个数据集上尝试一下Slope One算法。 MovieLens数据集是由明尼苏达州大学的GroupLens研究项目收集的,是用户对电影的评分。这个数据集可以在http://www.grouplens.org下载,有三种大小,这里我使用的是最小的那个,包含了943位用户对1682部电影的评价,约10万条记录。
df = pd.read_csv('./datamining/ml-latest-small/ratings.csv')
result = {}
for item in df.iterrows():
if not result.get(item[1]['userId']):
result[item[1]['userId']] = {}
result[item[1]['userId']][item[1]['movieId']] = item[1]['rating']
print(len(result))
df = pd.DataFrame(result)
610
class recommender:
def __init__(self,data):
self.frequency={}
self.deviation={}
self.data=data
#计算所有item之间评分偏差
def computeDeviation(self):
for ratings in self.data.values():
for item,rating in ratings.items():
self.frequency.setdefault(item,{})
self.deviation.setdefault(item,{})
for item2,rating2 in ratings.items():
if item!=item2:
self.frequency[item].setdefault(item2,0)
self.deviation[item].setdefault(item2,0.0)
self.frequency[item][item2]+=1#两个项目的用户数
self.deviation[item][item2]+=(rating-rating2)#累加两个评分差值
for item,ratings in self.deviation.items():
for item2 in ratings:
ratings[item2]/=self.frequency[item][item2]
#评分预测
def predictRating(self,userRatings,k):
recommendations={}
frequencies={}
for item,rating in userRatings.items():
for diffItem,diffRating in self.deviation.items():
if diffItem not in userRatings and item in self.deviation[diffItem]:
fre=self.frequency[diffItem][item]
recommendations.setdefault(diffItem,0.0)
frequencies.setdefault(diffItem,0)
#分子部分
recommendations[diffItem]+=(diffRating[item]+rating)*fre
#分母部分
frequencies[diffItem]+=fre
recommendations=[(k,v/frequencies[k]) for (k,v) in recommendations.items()]
#排序返回前k个
recommendations.sort(key=lambda a_tuple:a_tuple[1],reverse=True)
return recommendations[:k]
r=recommender(result)
r.computeDeviation()
u=result[5]
print(r.predictRating(u,5))
[(5746.0, 9.5), (6835.0, 9.5), (5764.0, 9.0), (7899.0, 9.0), (136850.0, 6.5)]
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。