数据从

数据训练集下载地址

https://grouplens.org/datasets/movielens/

下载,我选的是1MB版本的数据,大约10W+评分,9K+电影,600+用户。

使用ratings.csv,格式为userId,movieId,评分,时间戳。

步骤:

1、解析CSV文件,构建训练集。

2、计算两两物品分差平均值。

3、预测指定用户未评分物品的评分,推荐TopN。

SlopeOne算法理解起来简单,实现起来也简单,按照论文的描述,推荐结果也很不错。

当然有个不是问题的问题,因为要计算两两物品的分差的平均值,所以这一段比较耗时间。

#!/usr/bin/python3
import math
import csv
import datetime
import heapq
import json
from tqdm import tqdm


def buildTarin():
    """
    处理数据集,
    users=用户ID:{物品ID,评分}
    items=物品ID:{用户ID,评分}
    itemUsers=物品ID:[用户ID集]
    userItems=用户ID:[物品ID集]
    allItems=所有物品ID集
    """
    startTime  = datetime.datetime.now()
    users = dict()
    items = dict()
    itemUsers = dict()
    userItems = dict()
    allItems = set()

    with open('ratings.csv') as f:
        f_csv = csv.reader(f)
        for row in f_csv:
            userId=int(row[0])
            itemId=int(row[1])
            score=float(row[2])
            
            users.setdefault(userId,{})
            users[userId][itemId] = score
            
            userItems.setdefault(userId,[])
            userItems[userId].append(itemId)

            items.setdefault(itemId,{})
            items[itemId][userId]=score

            itemUsers.setdefault(itemId,[])
            itemUsers[itemId].append(userId)

            allItems.add(itemId)
    endTime=datetime.datetime.now()
    print('处理数据集耗时:{0}秒'.format((endTime-startTime).seconds))
    return users,userItems,items,itemUsers,sorted(allItems)

def avgDiffs(users,userItems,items,itemUsers,allItems):
    """
    计算两两物品间评分差的均值
    itemABMatrix=物品IDA:{物品IDB:平均值}
    """
    startTime  = datetime.datetime.now()
    itemABMatrix=dict()
    allItemSize=len(allItems)
    pbar = tqdm(allItems)
    for itemIdA in pbar:#遍历所有物品,计算两两评分差的均值
        pbar.set_description("Processing %s" % itemIdA)
        itemABMatrix.setdefault(itemIdA,{})
        itemUsersA=itemUsers[itemIdA]
        for  itemIdB in allItems:
            if itemIdA==itemIdB:#物品相同,不作处理
                continue
            itemUsersB=itemUsers[itemIdB]
            itemUsersAB=[x for x in itemUsersB if x in itemUsersA]#同时给物品AB评分的用户集
            itemUsersABSize=len(itemUsersAB)#同时给物品AB评分的用户数
            if itemUsersABSize==0:#没有同时给AB评分的用户,设为0
                itemABMatrix[itemIdA][itemIdB]=0.0
                continue
            sum=0.0#差值的和
            avg=0.0#差值平均值
            for userId in itemUsersAB:#遍历同时给物品AB评分的用户,计算每个用户给AB物品评分的差值之和
                scoreA=users[userId][itemIdA]
                scoreB=users[userId][itemIdB]
                sum=sum+scoreA-scoreB
            avg=sum/itemUsersABSize
            itemABMatrix[itemIdA][itemIdB]=avg
    #print(json.dumps(itemABMatrix, sort_keys=False, indent=4, separators=(',', ':')))
    endTime=datetime.datetime.now()
    print('计算评分差均值耗时:{0}秒'.format((endTime-startTime).seconds))
    return itemABMatrix

def recommendation(itemABMatrix,users,userItems,itemUsers,allItems,userId,N):
    """
    给用户推荐物品列表
    Args:

    """
    startTime  = datetime.datetime.now()
    pScore=dict()
    ratedItem=userItems[userId]
    notRatedItem=[x for x in allItems if x not in ratedItem]#用户未评过分的物品集
    #遍历用户未评过分的物品,计算预测分。
    #公式:设,评过分的物品A、B,未评过分的物品C
    #[同时给AC评过分的人数*(用户对A的评分-物品AC评分差均值)+同时给BC评过分的人数*(用户对B的评分-物品BC评分差均值)]/同时给AC评过分的人数+同时给BC评过分的人数
    for notRatedItemId in notRatedItem:
        sum=0.0
        avg=0.0
        allUserSize=0
        for ratedItemId in ratedItem:
            itemUsersA=itemUsers[ratedItemId]
            itemUsersB=itemUsers[notRatedItemId]
            itemUsersAB=[x for x in itemUsersB if x in itemUsersA]#同时给物品AB评分的用户集
            itemUsersABSize=len(itemUsersAB)#同时给物品AB评分的用户数
            
            allUserSize=allUserSize+itemUsersABSize
            sum=sum+itemUsersABSize*(users[userId][ratedItemId]-itemABMatrix[ratedItemId][notRatedItemId])
        pScore.setdefault(notRatedItemId,0.0)
        if allUserSize==0:#没有同时给物品评分的人,无法预测,不作处理
            continue
        
        avg=sum/allUserSize        
        pScore[notRatedItemId]=avg
        endTime=datetime.datetime.now()
    print('推荐耗时:{0}秒'.format((endTime-startTime).seconds))
    return heapq.nlargest(N, pScore.items(), key=lambda x: x[1])

if __name__ == "__main__":
    startTime  = datetime.datetime.now()
    users,userItems,items,itemUsers,allItems = buildTarin()
    #print(json.dumps(users, sort_keys=False, indent=4, separators=(',', ':')))
    #print(json.dumps(userItems, sort_keys=False, indent=4, separators=(',', ':')))
    #print(json.dumps(items, sort_keys=False, indent=4, separators=(',', ':')))
    #print(json.dumps(itemUsers, sort_keys=False, indent=4, separators=(',', ':')))
    #print(json.dumps(allItems, sort_keys=False, indent=4, separators=(',', ':')))
    itemABMatrix=avgDiffs(users,userItems,items,itemUsers,allItems)
    topN=recommendation(itemABMatrix,users,userItems,itemUsers,allItems,3,5)
    endTime=datetime.datetime.now()
    print('总耗时:{0}秒'.format((endTime-startTime).seconds))
    print(topN)

  

结果: