数据分析程序员读书

提供推荐(集体智慧) —— 使用MovieLens数据为用户推荐

2016-07-20  本文已影响2400人  _简书

0x00 下载MovieLens数据

1) 从网站http://grouplens.org/datasets/movielens/下载数据
2) u.item文件包含两列数据,为电影id和电影名称的对应关系;u.data文件包含四列数据,为用户id,电影id,评价,时间

2016-07-20 21:25:24 的屏幕截图.png
3)读入数据
<code>
movie_list = {} #id:title
with open("./u.item") as f:
    for line in f.readlines():
        (mid, title) = line.split('|')[0:2]
        movie_list[mid] = title
pref_by_people = {}
with open("./u.data") as f:
    for line in f.readlines():
        (uid, mid, rating) = line.split('\t')[0:3]
        if not uid in pref_by_people.keys():
            pref_by_people[uid] = {}
        pref_by_people[uid][movie_list[mid]] = int(rating)

</code>
4)数据类型转换 {people:{movie:1}} -->> {movie:{people:1}}
<code>

def TransfromPref(pref):
    re_pref = {}
    for k1, v1 in pref.items():
        for k2, v2 in v1.items():
            if not k2 in re_pref.keys():
                re_pref[k2] = {}
            re_pref[k2][k1] = v2
    return re_pref

pref_by_movie = TransfromPref(pref_by_people)

0x01 协同过滤两种方式

协同过滤:常常被用于分辨某位特定顾客可能感兴趣的东西,这些结论来自于对其他相似顾客对哪些产品感兴趣的分析。

欧几里德距离评价

1)以人们对事物的评价作为坐标轴,比较两人在坐标空间的距离,来判断人们偏好的相近程度
2)代码
<code>

def edclidean(prefs, person1, person2):
    #找出两人均做出评价的事物
    both_list = [item for item in prefs[person1].keys() if item in prefs[person2].keys()] 
    #计算距离
    dis = math.sqrt(sum([(prefs[person1][item] - perfs[person2][item]) ** 2 for item in both_list])) 
    #以距离的倒数为返回值,两人偏好程度越就近,该值越大,假设不存在完全相同的两人,不考虑dis为0的情况
    return 1/dis 

</code>
prefs中以python字典嵌套字典的方式存储了每个人对于某时候的评价
prefs = {person:{item:1...}...},下同

皮尔逊相关系数评价

1)考虑到人们对于事物的评价标准不同,即有些人倾向打出更高的分数,而其他人更倾向于打出较低的分数,这是单纯的考虑逻辑空间距离就不能很好的得出相近的偏好程度(例:person1对item1和item2打分分别为6和8,person2对item1和item2打分分别为8和10,两人分数不相同,但两人的评价有相同的趋势,此时也认为两人偏好相同)
2)皮尔逊相关系数计算

2016-07-20 20:04:24 的屏幕截图.png

3)代码
<code>
通过每个电影人们的评价计算出电影之间的相关系数,将上例中通过中每个人对电影的评价计算人们之间的相关系数同理

def Pearson(pref , movie1, movie2):
#找出对两部电影都评论的人
    people_list = [person for person in pref[movie1].keys() if person in pref[movie2].keys()]
    n = len(people_list)
    if n == 0:
        return 0
    #计算评价和
    sum1 = sum([pref[movie1][person] for person in people_list])
    sum2 = sum([pref[movie2][person] for person in people_list])
    #计算评价平方和
    sumSq1 = sum([pref[movie1][person] ** 2 for person in people_list])
    sumSq2 = sum([pref[movie2][person] ** 2 for person in people_list])
    #计算评价成绩和
    psum = sum([pref[movie1][person] * pref[movie2][person] for person in people_list])
    # 皮尔逊相关系数计算
    num = psum - sum1 * sum2 / n
    den = sqrt((sumSq1 - (sum1 ** 2) / n) * (sumSq2 - (sum2 ** 2) / n))

    if den == 0:
        return 0
    return num / den

</code>

0x02 根据电影推荐相关的电影

1)通过皮尔逊相关系数计算出每个电影的最相近的电影
<code>

def TopMatch(pref, movie, n = 5):
    #计算给电影和每部电影的皮尔逊相关系数
    scores = [(Pearson(pref_by_movie, movie, mov), mov) for mov in pref_by_movie.keys() if mov != movie]
    #根据系数进行排序,并由大到小排序
    scores.sort(key = lambda x:x[0], reverse = True)
    return scores[0:n]

</code>
2)对每部电影计算最接近电影,得到match_list
<code>

def CreateMatchList(pref = pref_by_movie):
    match_list = {}
    for movie in pref.keys():
        match_list[movie] = TopMatch(pref, movie, 5)
    return match_list

match_list = CreateMatchList()

</code>

0x03 为用户推荐电影

1)找到我们已经看过的电影和评价,从match_list中找出那些和我们看过的影片相近程度更高的没看过的电影,用相关系数作为权值,计算每部电影的评分的加权平均值。

2016-07-20 20:41:09 的屏幕截图.png
例:每一横列出我们看过的电影第一列是我们对它的评分,之后的没两列分别是新电影的相关系数和系数乘评分的值,总计记录的相关系数的和与相关系数与评分的乘机的和,最后一横做除法得到评分的加权平均数,即推荐得分,分数越高的越值得推荐
2)代码
<code>
def get_recommanded_items(pref = pref_by_people, match_list = match_list, user = '1'):
    try:
        user_ratings = pref[user] #找出用户看过的电影与评价
    except KeyError:
        print("no user")
        return 0
    scores = {} #记录加权和
    totalsim = {} #记录评分和

    for movie, rating in user_ratings.items(): #遍历当前用户评分电影
        for sim, sim_movie in match_list[movie]: #遍历当前电影相近电影
            if sim_movie in user_ratings.keys(): #如果用户看过该电影,跳出本次循环
                continue
             #记录加权和与评分和
            if not sim_movie in scores.keys():
                scores[sim_movie] = sim * rating
                totalsim[sim_movie] = sim 
            scores[sim_movie] += sim * rating
            totalsim[sim_movie] += sim

    rankings = [(scores[sim_movie]/totalsim[sim_movie], sim_movie) for sim_movie in scores.keys() if totalsim[sim_movie] != 0]
    #排序并取前5
    rankings.sort(key=lambda x:x[0], reverse=True)
    return rankings[0:5]

</code>

0x04 基于用户进行过滤还是基于物品进行过滤

1)本文采用了基于物品方式的过滤,即对每样物品计算和它相似的物品。当为某位用户推荐时,我们找到他过去评分靠前的物品,再找出这些物品相似的物品,用相关系数作为权值,计算每个物品的评分的加权平均值
2)基于用户的推荐即对每位用户求出和他取向相近的人,找出这些人中评分靠前的物品,用相关系数作为权值,计算每个物品的评分的加权平均值
3)物品间相似度的变化不会向人之间变化那么快,即相关系数计算不用那么频繁,可以事先计算好,不用在每次推荐时都计算
4)针对大量数据推荐列表时,基于物品的过滤明显比基于用户的过滤快,不过需要额外的存储空间开销。
5)对于稀疏数据集,基于物品的推荐明显优于基于用户的推荐;对于密集数据集,两者效果类似

0x05 测试

2016-07-20 21:11:26 的屏幕截图.png
输入用户id得到推荐电影的前5位
第一次推荐由于需要计算match_list,花费较长时间
代码下载:链接: http://pan.baidu.com/s/1jILdXtk 密码: xyzd
上一篇下一篇

猜你喜欢

热点阅读