# 协同过滤算法

jopen 7年前

• 搜集偏好

• 寻找相近用户

• 推荐物品

### 搜集偏好

#生成用户偏好字典  def make_data():   result={}   f = open('data/u.data', 'r')   lines = f.readlines()   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替换为电影名 构造数据集  def loadMovieLens(path='data'):   # Get movie titles   movies={}   for line in open(path+'/u.item'):    (id,title)=line.split('|')[0:2]    movies[id]=title   # Load data   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

</div>

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. 与频繁模式挖掘的区别