# 协同过滤算法

• 搜集偏好

• 寻找相近用户

• 推荐物品

### 搜集偏好

#生成用户偏好字典
def make_data():
result={}
f = open('data/u.data', 'r')
for line in lines:
#按行分割数据
(userId , itemId , score,time ) = line.strip().split("\t")
#字典要提前定义
if not result.has_key( userId ):
result[userId]={}
#注意float,不然后续的运算存在类型问题
result[userId][itemId] = float(score)
return result

#将id替换为电影名 构造数据集
# Get movie titles
movies={}
for line in open(path+'/u.item'):
(id,title)=line.split('|')[0:2]
movies[id]=title

prefs={}
for line in open(path+'/u.data'):
(user,movieid,rating,ts)=line.split('\t')
prefs.setdefault(user,{})
prefs[user][movies[movieid]]=float(rating)
return prefs

#用户：{电影名称：评分}
critics={
'Lisa Rose': {'Lady in the Water': 2.5, 'Snakes on a Plane': 3.5,'Just My Luck': 3.0, 'Superman Returns': 3.5, 'You, Me and Dupree': 2.5,'The Night Listener': 3.0},
'Gene Seymour': {'Lady in the Water': 3.0, 'Snakes on a Plane': 3.5,'Just My Luck': 1.5, 'Superman Returns': 5.0, 'The Night Listener': 3.0,'You, Me and Dupree': 3.5},
'Michael Phillips':{'Lady in the Water': 2.5, 'Snakes on a Plane': 3.0,'Superman Returns': 3.5, 'The Night Listener': 4.0},
'Claudia Puig': {'Snakes on a Plane': 3.5, 'Just My Luck': 3.0,'The Night Listener': 4.5, 'Superman Returns': 4.0,'You, Me and Dupree': 2.5},
'Mick LaSalle': {'Lady in the Water': 3.0, 'Snakes on a Plane': 4.0, 'Just My Luck': 2.0, 'Superman Returns': 3.0, 'The Night Listener': 3.0,
'You, Me and Dupree': 2.0},
'Jack Matthews': {'Lady in the Water': 3.0, 'Snakes on a Plane': 4.0,'The Night Listener': 3.0, 'Superman Returns': 5.0, 'You, Me and Dupree': 3.5},
'Toby': {'Snakes on a Plane':4.5,'You, Me and Dupree':1.0,'Superman Returns':4.0}
}

### 欧几里得距离

$$|AB|=\sqrt{(x_1 - x_2)^2+(y_1 - y_2)^2+...+(x_n-y_n)^2}$$

from math import sqrt
sqrt(pow( 4.5-4 , 2 ) + pow( 1 - 2 , 2))
1.118033988749895

1/(1+sqrt(pow( 4.5-4 , 2 ) + pow( 1 - 2 , 2)))
0.4721359549995794

#欧几里得距离
def sim_distance( prefs,person1,person2 ):
si={}
for itemId in prefs[person1]:
if itemId in prefs[person2]:
si[itemId] = 1
#no same item
if len(si)==0: return 0
sum_of_squares = 0.0

#计算距离
sum_of_squares=sum([pow(prefs[person1][item]-prefs[person2][item],2) for item in si])
return 1/(1 + sqrt(sum_of_squares) )

sim_distance( critics , 'Toby', 'Mick LaSalle' )
0.307692307692

### 皮尔逊相关度

$$\frac{\sum x_iy_i-\frac{\sum x_i\sum y_i}{n}}{\sqrt{\sum x_i^2-\frac{(\sum x_i)^2}{n}}\sqrt{\sum y_i^2-\frac{(\sum y_i)^2}{n}}}$$

python代码实现：

#皮尔逊相关度
def sim_pearson(prefs,p1,p2):
si={}
for item in prefs[p1]:
if item in prefs[p2]: si[item]=1

if len(si)==0: return 0

n=len(si)

#计算开始
sum1=sum([prefs[p1][it] for it in si])
sum2=sum([prefs[p2][it] for it in si])

sum1Sq=sum([pow(prefs[p1][it],2) for it in si])
sum2Sq=sum([pow(prefs[p2][it],2) for it in si])

pSum=sum([prefs[p1][it]*prefs[p2][it] for it in si])

num=pSum-(sum1*sum2/n)
den=sqrt((sum1Sq-pow(sum1,2)/n)*(sum2Sq-pow(sum2,2)/n))
#计算结束

if den==0: return 0

r=num/den

return r


recommendations.sim_pearson(recommendations.critics,'Gene Seymour','Lisa Rose')

### 为评论者打分

#推荐用户
def topMatches(prefs,person,n=5,similarity=sim_distance):
#python列表推导式
scores=[(similarity(prefs,person,other),other) for other in prefs if other!=person]
scores.sort()
scores.reverse()
return scores[0:n]

### 推荐物品

#基于用户推荐物品
def getRecommendations(prefs,person,similarity=sim_pearson):

totals={}
simSums={}

for other in prefs:
#不和自己做比较
if other == person:
continue
sim = similarity( prefs,person,other )
#去除负相关的用户
if sim<=0: continue
for item in prefs[other]:
#只对自己没看过的电影做评价
if item in prefs[person]:continue
totals.setdefault( item ,0 )
totals[item] += sim*prefs[other][item]
simSums.setdefault(item,0)
simSums[item] += sim
#归一化处理生成推荐列表
rankings=[(totals[item]/simSums[item],item) for item in totals]
#rankings=[(total/simSums[item],item) for item,total in totals.items()]
rankings.sort()
rankings.reverse()
return rankings

### 基于物品的协同过滤

#基于物品的列表
def transformPrefs(prefs):
itemList ={}
for person in prefs:
for item in prefs[person]:
if not itemList.has_key( item ):
itemList[item]={}
#result.setdefault(item,{})
itemList[item][person]=prefs[person][item]
return itemList

#构建基于物品相似度数据集
def calculateSimilarItems(prefs,n=10):
result={}
itemPrefs=transformPrefs(prefs)
c = 0
for item in itemPrefs:
c += 1
if c%10==0: print "%d / %d" % (c,len(itemPrefs))
scores=topMatches(itemPrefs,item,n=n,similarity=sim_distance)
result[item]=scores
return result

#基于物品的推荐
def getRecommendedItems(prefs,itemMatch,user):
userRatings=prefs[user]
scores={}
totalSim={}
# Loop over items rated by this user
for (item,rating) in userRatings.items( ):
# Loop over items similar to this one
for (similarity,item2) in itemMatch[item]:

# Ignore if this user has already rated this item
if item2 in userRatings: continue
# Weighted sum of rating times similarity
scores.setdefault(item2,0)
scores[item2]+=similarity*rating
# Sum of all the similarities
totalSim.setdefault(item2,0)
totalSim[item2]+=similarity

# Divide each total score by total weighting to get an average
rankings=[(score/totalSim[item],item) for item,score in scores.items( )]

# Return the rankings from highest to lowest
rankings.sort( )
rankings.reverse( )
return rankings    

### 思考

1. UserCF和ItemCF的比较

2. 归一化处理的更合适方法

3. 与频繁模式挖掘的区别