【笔记4】用pandas实现条目数据格式的推荐算法 (基于用户的协同)

'''
基于用户的协同推荐

条目数据
'''

import pandas as pd
from io import StringIO
import json

#数据类型一:条目(用户、商品、打分)(避免巨型稀疏矩阵)
csv_txt = '''"Angelica","Blues Traveler",3.5
"Angelica","Broken Bells",2.0
"Angelica","Norah Jones",4.5
"Angelica","Phoenix",5.0
"Angelica","Slightly Stoopid",1.5
"Angelica","The Strokes",2.5
"Angelica","Vampire Weekend",2.0
"Bill","Blues Traveler",2.0
"Bill","Broken Bells",3.5
"Bill","Deadmau5",4.0
"Bill","Phoenix",2.0
"Bill","Slightly Stoopid",3.5
"Bill","Vampire Weekend",3.0
"Chan","Blues Traveler",5.0
"Chan","Broken Bells",1.0
"Chan","Deadmau5",1.0
"Chan","Norah Jones",3.0
"Chan","Phoenix",5,
"Chan","Slightly Stoopid",1.0
"Dan","Blues Traveler",3.0
"Dan","Broken Bells",4.0
"Dan","Deadmau5",4.5
"Dan","Phoenix",3.0
"Dan","Slightly Stoopid",4.5
"Dan","The Strokes",4.0
"Dan","Vampire Weekend",2.0
"Hailey","Broken Bells",4.0
"Hailey","Deadmau5",1.0
"Hailey","Norah Jones",4.0
"Hailey","The Strokes",4.0
"Hailey","Vampire Weekend",1.0
"Jordyn","Broken Bells",4.5
"Jordyn","Deadmau5",4.0
"Jordyn","Norah Jones",5.0
"Jordyn","Phoenix",5.0
"Jordyn","Slightly Stoopid",4.5
"Jordyn","The Strokes",4.0
"Jordyn","Vampire Weekend",4.0
"Sam","Blues Traveler",5.0
"Sam","Broken Bells",2.0
"Sam","Norah Jones",3.0
"Sam","Phoenix",5.0
"Sam","Slightly Stoopid",4.0
"Sam","The Strokes",5.0
"Veronica","Blues Traveler",3.0
"Veronica","Norah Jones",5.0
"Veronica","Phoenix",4.0
"Veronica","Slightly Stoopid",2.5
"Veronica","The Strokes",3.0'''

#数据类型二:json数据(用户、商品、打分)
json_txt = '''{"Angelica": {"Blues Traveler": 3.5, "Broken Bells": 2.0,
                      "Norah Jones": 4.5, "Phoenix": 5.0,
                      "Slightly Stoopid": 1.5,
                      "The Strokes": 2.5, "Vampire Weekend": 2.0},

         "Bill":{"Blues Traveler": 2.0, "Broken Bells": 3.5,
                 "Deadmau5": 4.0, "Phoenix": 2.0,
                 "Slightly Stoopid": 3.5, "Vampire Weekend": 3.0},

         "Chan": {"Blues Traveler": 5.0, "Broken Bells": 1.0,
                  "Deadmau5": 1.0, "Norah Jones": 3.0, "Phoenix": 5,
                  "Slightly Stoopid": 1.0},

         "Dan": {"Blues Traveler": 3.0, "Broken Bells": 4.0,
                 "Deadmau5": 4.5, "Phoenix": 3.0,
                 "Slightly Stoopid": 4.5, "The Strokes": 4.0,
                 "Vampire Weekend": 2.0},

         "Hailey": {"Broken Bells": 4.0, "Deadmau5": 1.0,
                    "Norah Jones": 4.0, "The Strokes": 4.0,
                    "Vampire Weekend": 1.0},

         "Jordyn":  {"Broken Bells": 4.5, "Deadmau5": 4.0,
                     "Norah Jones": 5.0, "Phoenix": 5.0,
                     "Slightly Stoopid": 4.5, "The Strokes": 4.0,
                     "Vampire Weekend": 4.0},

         "Sam": {"Blues Traveler": 5.0, "Broken Bells": 2.0,
                 "Norah Jones": 3.0, "Phoenix": 5.0,
                 "Slightly Stoopid": 4.0, "The Strokes": 5.0},

         "Veronica": {"Blues Traveler": 3.0, "Norah Jones": 5.0,
                      "Phoenix": 4.0, "Slightly Stoopid": 2.5,
                      "The Strokes": 3.0}
}'''

df = None

#方式一:加载csv数据
def load_csv_txt():
    global df
    df = pd.read_csv(StringIO(csv_txt), header=None, names=['user','goods','rate'])

#方式二:加载json数据(把json读成条目)
def load_json_txt():
    global df
    #由json数据得到字典
    users = json.loads(json_txt)

    #遍历字典,得到条目
    csv_txt_ = ''
    for user in users:
        for goods in users[user]:
            csv_txt_ += '{},{},{}\n'.format(user, goods, users[user][goods])

    df = pd.read_csv(StringIO(csv_txt_), header=None, names=['user','goods','rate'])

print('测试:读取数据')
#load_csv_txt()
load_json_txt()

def build_xy(user_name1, user_name2):
    df1 = df.ix[df['user'] == user_name1, ['goods','rate']]
    df2 = df.ix[df['user'] == user_name2, ['goods','rate']]

    df3 = pd.merge(df1, df2, on='goods', how='inner') #只保留两人都有评分的商品的评分

    return df3['rate_x'], df3['rate_y'] #merge之后默认的列名:rate_x,rate_y

#曼哈顿距离
def manhattan(user_name1, user_name2):
    x, y = build_xy(user_name1, user_name2)
    return sum(abs(x - y))

#欧几里德距离
def euclidean(user_name1, user_name2):
    x, y = build_xy(user_name1, user_name2)
    return sum((x - y)**2)**0.5

#闵可夫斯基距离
def minkowski(user_name1, user_name2, r):
    x, y = build_xy(user_name1, user_name2)
    return sum(abs(x - y)**r)**(1/r)

#皮尔逊相关系数
def pearson(user_name1, user_name2):
    x, y = build_xy(user_name1, user_name2)
    mean1, mean2 = x.mean(), y.mean()
    #分母
    denominator = (sum((x-mean1)**2)*sum((y-mean2)**2))**0.5
    return [sum((x-mean1)*(y-mean2))/denominator, 0][denominator == 0]

#余弦相似度(数据的稀疏性问题,在文本挖掘中应用得较多)
def cosine(user_name1, user_name2):
    x, y = build_xy(user_name1, user_name2)
    #分母
    denominator = (sum(x*x)*sum(y*y))**0.5
    return [sum(x*y)/denominator, 0][denominator == 0]

metric_funcs = {
    'manhattan': manhattan,
    'euclidean': euclidean,
    'minkowski': minkowski,
    'pearson': pearson,
    'cosine': cosine
}

print('\n测试:计算Angelica与Bill的曼哈顿距离')
print(manhattan('Angelica','Bill'))

#计算最近的邻居(返回:pd.Series)
def computeNearestNeighbor(user_name, metric='pearson', k=3, r=2):
    '''
    metric: 度量函数
    k:      返回k个邻居
    r:      闵可夫斯基距离专用

    返回:pd.Series,其中index是邻居名称,values是距离
    '''
    array = df[df['user'] != user_name]['user'].unique()
    if metric in ['manhattan', 'euclidean']:
        return pd.Series(array, index=array.tolist()).apply(metric_funcs[metric], args=(user_name,)).nsmallest(k)
    elif metric in ['minkowski']:
        return pd.Series(array, index=array.tolist()).apply(metric_funcs[metric], args=(user_name, r,)).nsmallest(k)
    elif metric in ['pearson', 'cosine']:
        return pd.Series(array, index=array.tolist()).apply(metric_funcs[metric], args=(user_name,)).nlargest(k)

print('\n测试:计算Hailey的最近邻居')
print(computeNearestNeighbor('Hailey'))

#向给定用户推荐(返回:pd.DataFrame)
def recommend(user_name):
    """返回推荐结果列表"""
    # 找到距离最近的用户名
    nearest_username = computeNearestNeighbor(user_name).index[0]

    # 找出这位用户评价过、但自己未曾评价的乐队
    df1 = df.ix[df['user'] == user_name, ['goods', 'rate']]
    df2 = df.ix[df['user'] == nearest_username, ['goods', 'rate']]

    df3 = pd.merge(df1, df2, on='goods', how='outer')

    return df3.ix[(df3['rate_x'].isnull()) & (df3['rate_y'].notnull()), ['goods', 'rate_y']].sort_values(by='rate_y')

print('\n测试:为Hailey做推荐')
print(recommend('Hailey'))

#向给定用户推荐(返回:pd.Series)
def recommend2(user_name, metric='pearson', k=3, n=5, r=2):
    '''
    metric: 度量函数
    k:      根据k个最近邻居,协同推荐
    r:      闵可夫斯基距离专用
    n:      推荐的商品数目

    返回:pd.Series,其中index是商品名称,values是加权评分
    '''
    # 找到距离最近的k个邻居
    nearest_neighbors = computeNearestNeighbor(user_name, metric='pearson', k=k, r=r)

    # 计算权值
    if metric in ['manhattan', 'euclidean', 'minkowski']: # 距离越小,越类似
        nearest_neighbors = 1 / nearest_neighbors # 所以,取倒数(或者别的减函数,如:y=2**-x)
    elif metric in ['pearson', 'cosine']:                 # 距离越大,越类似
        pass

    nearest_neighbors = nearest_neighbors / nearest_neighbors.sum() #已经变为权值

    # 逐个邻居找出其评价过、但自己未曾评价的乐队(或商品)的评分,并乘以权值
    neighbors_rate_with_weight = []
    for neighbor_name in nearest_neighbors.index:
        # 每个结果:pd.Series,其中index是商品名称,values是评分(已乘权值)
        df1 = df.ix[df['user'] == user_name, ['goods', 'rate']]
        df2 = df.ix[df['user'] == neighbor_name, ['goods', 'rate']]

        df3 = pd.merge(df1, df2, on='goods', how='outer')

        df4 =  df3.ix[(df3['rate_x'].isnull()) & (df3['rate_y'].notnull()), ['goods', 'rate_y']]

        #注意这中间有一个转化为pd.Series的操作!
        neighbors_rate_with_weight.append(pd.Series(df4['rate_y'].tolist(), index=df4['goods']) * nearest_neighbors[neighbor_name])

    # 把邻居们的加权评分拼接成pd.DataFrame,按列累加,取最大的前n个商品的评分
    return pd.concat(neighbors_rate_with_weight, axis=1).sum(axis=1, skipna=True).nlargest(n) # 黑科技!

print('\n测试:为Hailey做推荐')
print(recommend2('Hailey', metric='manhattan', k=3, n=5))

print('\n测试:为Hailey做推荐')
print(recommend2('Hailey', metric='euclidean', k=3, n=5, r=2))

print('\n测试:为Hailey做推荐')
print(recommend2('Hailey', metric='pearson', k=1, n=5))
时间: 2024-07-31 02:51:48

【笔记4】用pandas实现条目数据格式的推荐算法 (基于用户的协同)的相关文章

【笔记3】用pandas实现矩阵数据格式的推荐算法 (基于用户的协同)

原书作者使用字典dict实现推荐算法,并且惊叹于18行代码实现了向量的余弦夹角公式. 我用pandas实现相同的公式只要3行. 特别说明:本篇笔记是针对矩阵数据,下篇笔记是针对条目数据. ''' 基于用户的协同推荐 矩阵数据 ''' import pandas as pd from io import StringIO import json #数据类型一:csv矩阵(用户-商品)(适用于小数据量) csv_txt = '''"user","Blues Traveler&qu

【笔记6】用pandas实现条目数据格式的推荐算法 (基于物品的协同)

''' 基于物品的协同推荐 矩阵数据 说明: 1.修正的余弦相似度是一种基于模型的协同过滤算法.我们前面提过,这种算法的优势之 一是扩展性好,对于大数据量而言,运算速度快.占用内存少. 2.用户的评价标准是不同的,比如喜欢一个歌手时有些人会打4分,有些打5分:不喜欢时 有人会打3分,有些则会只给1分.修正的余弦相似度计算时会将用户对物品的评分减去 用户所有评分的均值,从而解决这个问题. 如何预测用户对给定物品的打分? 一.基于用户协同 方法1:用户之间的距离/相似度(K近邻算法) 二.基于物品协

【笔记5】用pandas实现矩阵数据格式的推荐算法 (基于物品的协同)

''' 基于物品的协同推荐 矩阵数据 说明: 1.修正的余弦相似度是一种基于模型的协同过滤算法.我们前面提过,这种算法的优势之 一是扩展性好,对于大数据量而言,运算速度快.占用内存少. 2.用户的评价标准是不同的,比如喜欢一个歌手时有些人会打4分,有些打5分:不喜欢时 有人会打3分,有些则会只给1分.修正的余弦相似度计算时会将用户对物品的评分减去 用户所有评分的均值,从而解决这个问题. ''' import pandas as pd from io import StringIO #数据类型一:

【笔记2】推荐算法中的数据格式

设计推荐算法时,首先要考虑数据格式. 由于数据是关于用户对商品的评分,其具有三个要素:用户.商品.评分 所以,数据格式有三种形式: 1. json格式(json文本) #数据格式:json数据(用户.商品.打分) json_txt = '''{"Angelica": {"Blues Traveler": 3.5, "Broken Bells": 2.0, "Norah Jones": 4.5, "Phoenix&qu

『 读书笔记 』4月读书总结|博文推荐

原文链接:『 读书笔记 』4月读书总结|博文推荐 写在前面 计划是每月读 5-10 本书,书籍类型大概是三个方面的:金融,技术,管理.之所以选择这三个方面,一方面是因为自己对这三个方面都很有兴趣,其次是被 linkedin 创始人 Hoffman 的 ABZ 理论 深度影响.建议大家都看看 abz 理论那篇文章,如果我有空,也会整理一些常用的这类理论模型到博客里的. 月底读书总结的形式都很简单,只是简单的一个列表和简单的书评,对觉得比较好的书会有单独的读书笔记.另外推荐大家用 excel 来做一

Ajax学习笔记---3种Ajax的实现方法【推荐】_AJAX相关

Ajax:  Asynchronous JavaScript and Xml , 异步js脚本和xml , 常用来实现页面局部的异步刷新, 对提高用户体验有很大帮助. Xml在多语言时较有优势, 但Ajax技术实际上较多采用Json对象而不是Xml来处理数据. (一) Ajax历史....了解性知识 Ajax归属于Web前端开发技术, 与javascript有着异常紧密的联系. Ajax就是一种实现异步通信无刷新的技术, 而这种技术可以有很多种实现方式. 浏览器的鼻祖网景(NetScape)公司

Ajax学习笔记---3种Ajax的实现方法【推荐】

Ajax:  Asynchronous JavaScript and Xml , 异步js脚本和xml , 常用来实现页面局部的异步刷新, 对提高用户体验有很大帮助. Xml在多语言时较有优势, 但Ajax技术实际上较多采用Json对象而不是Xml来处理数据. (一) Ajax历史....了解性知识 Ajax归属于Web前端开发技术, 与javascript有着异常紧密的联系. Ajax就是一种实现异步通信无刷新的技术, 而这种技术可以有很多种实现方式. 浏览器的鼻祖网景(NetScape)公司

B2C网站设计理论:推荐商品页面的建立

推荐系统广泛应用于各类网站,电子商务中的商品推荐.博客网站的文章推荐,以及帮助人们寻找音乐和影片的各类应用.但如何才能从无到有的给网站配备一个推荐系统呢?针对这个问题,我在搜索引擎中遍寻多时,但始终没有找到满意的答案.这期间我也加入了国内推荐系统高手聚集的推荐系统邮件列表,其中不乏当当.卓越亚马逊.豆瓣等业内在推荐系统上领先的产品.技术高手,但浸淫多日却始终无法在脑海中形成一个以内容推荐为最终目的的产品框架或产品路线图.这种状态一直持续到我购买了集体智慧编程(Programming Collec

《python 与数据挖掘 》 一 导读

前 言 为什么要写本书? Python是什么? Python是一种带有动态语义的.解释性的.面向对象的高级编程语言.其高级内置数据结构,结合动态类型和动态绑定,使其对于敏捷软件开发非常具有吸引力.同时,Python作为脚本型(胶水)语言连接现有的组件也十分高效.Python语法简洁,可读性强,从而能降低程序的维护成本.不仅如此,Python支持模块和包,鼓励程序模块化和代码重用. Python语言的解释性使其语法更接近人类的表达和思维过程,开发程序的效率极高.习惯使用Python者,总习惯在介绍