最近无意间看到了面向程序员的数据挖掘导论,看了第一段就感觉很简单粗暴,看了第一章,记录一下吧~
第一章主要内容:
就是根据他人的兴趣爱好来推荐,工作原理大概就是:我想要给你推荐一首歌,我会在所有有记录用户找一个最和你相似的,然后根据他的爱好来进行推荐。
辣么怎么样来找到最和你相似的用户呢?很多方法啊,先说根据距离计算:
曼哈顿距离
欧几里得距离
这两种方法都有一个问题,就是只能采用同时都有评价记录的数据。
将这两种方法可以推广成闵可夫斯基距离,公式是:
其中,r=1即为曼哈顿距离,r=2就是欧几里得距离。。。
注意
r值越大,单个维度的差值大小会对整体距离有更大的影响。
由每个用户的打分标准非常不同,所以有“分数膨胀”的现象产生。例如:
解决方法之一就是使用皮尔逊相关系数,从图表上理解,两个用户意见相一致表现为一条直线。皮尔逊相关系数用于衡量两个变量之间的相关性,它的值在-1至1之间,1表示完全吻合,-1表示完全相悖。我们利用这一点来找到相似的用户。
皮尔逊相关系数的公式:
上面的公式除了看起来比较复杂,另一个问题是要获得计算结果必须对数据做多次遍历。一般使用另外一个近似公式计算皮尔逊相关系数的近似值,比较复杂,就不列出了。其最大的优点是可以在实现时仅遍历一边数据。
余弦相似度。它在文本挖掘中应用得较多,在协同过滤中也会使用到。
当单个用户的数据是稀疏的,即非零值较总体要少得多。
类似的情况是在计算两篇文章的相似度时。比如说我们想找一本和《The Space Pioneers》相类似的书,方法之一是利用单词出现的频率,即统计每个单词在书中出现的次数占全书单词的比例,如“the”出现频率为6.13%,“Tom” 0.89%,“space” 0.25%。我们可以用这些数据来寻找一本相近的书。但是,这里同样有数据的稀疏性问题。《The Space Pioneers》中有6629个不同的单词,但英语语言中有超过100万个单词,这样一来非零值就很稀少了,也就不能计算两本书之间的距离。
余弦相似度的计算中会略过这些零值匹配。它的计算公式是:
其中,“·”号表示数量积。“||x||”表示向量x的模,计算公式是: ∑ni=1x2i−−−−−−−√
余弦相似度的范围从1到-1,1表示完全匹配,-1表示完全相悖。
只依靠最相似的 一个 用户来做推荐,如果这个用户有些特殊的偏好,就会直接反映在推荐内容里,这样推荐效果并不是最好的。解决方法之一是找寻多个相似的用户,这里就要用到K最邻近算法了。
最后把书里的题目实现了一下:
本书的网站上有一个包含25部电影评价的数据集,实现一个推荐算法。
#-*-encoding:utf-8-*-
import csv
from math import sqrt
class recommender:
def __init__(self,data={},k=3,metric='pearson',n=5):
self.data=data
self.k=k
self.metric=metric
self.n=n
if metric=='pearson':
self.cal=self.metric
def loadData(self,path=''):
self.data={}
users=[]
i = 0
with open(path+'Movie_Ratings.csv', newline='',encoding='utf-8') as f:
reader = csv.reader(f)
for line in reader:
if i==0:
fields=str(line).split(',')
for x in fields:
if x!='':
key=x.strip('\ []"').strip("'").strip()
if key!='':
self.data[key]={}
users.append(key)
i=i+1
else:
fields=str(line).split(",")
filmname=fields[0].strip().strip('\[]\'"')
for j in range(1,len(fields)):
if fields[j].strip().strip('\[]\'"')!='':
self.data[users[j-1]][filmname]=fields[j]
def pearson(self,rating1={}, rating2={}):
sum_xiyi,n=0.0,0
sum_xi,sum_yi=0.0,0.0
sum_xi2,sum_yi2=0.0,0.0
for x1 in rating1:
if x1 in rating2:
x,y=int(rating1[x1].strip().strip('\[]\'"')),int(rating2[x1].strip().strip('\[]\'"'))
sum_xiyi+=x*y
sum_xi+=x
sum_yi+=y
sum_xi2+=pow(x,2)
sum_yi2+=pow(y,2)
n+=1
denominator=sqrt(sum_xi2-pow(sum_xi,2)/n)*sqrt(sum_yi2-pow(sum_yi,2)/n)
if denominator==0:
return 0
else:
return (sum_xiyi-(sum_xi*sum_yi)/n)/denominator
def computeNearestNeighbor(self,username):
nearestList=[]
for user in self.data:
if user!=username:
dis=self.pearson(self.data[user],self.data[username])
nearestList.append([user,dis])
return sorted(nearestList,key=lambda x:nearestList[1],reverse=True)
def recommend(self,username):
nearestList=self.computeNearestNeighbor(username)
totalDistance=0.0
userrating=self.data[username]
recommendations={}
#先计算权重ss
for i in range(self.k):
totalDistance+=nearestList[i][1]
#选出前k个和指定user接近的,汇总评分
for i in range(self.k):
weight=nearestList[i][1]/totalDistance
#获取第i个用户的评分记录
neighborRating=self.data[nearestList[i][0]]
#获得第I个邻居评价了但user没有评价过的电影,并存入其加权评分
for film in neighborRating:
if film not in userrating:
if film not in recommendations:
recommendations[film]=float(neighborRating[film].strip().strip('\[]\'"'))*weight
else:
recommendations[film]+=float(neighborRating[film].strip().strip('\[]\'"'))*weight
#开始推荐
recommendations=list(recommendations.items())[:self.n]
return sorted(recommendations,key=lambda x:x[1],reverse=True)
if __name__ == '__main__':
r=recommender()
r.loadData('D:\\')
print(r.recommend('vanessa'));