Python中使用pandas数据框和嵌套for循环的基于项目的协作过滤器的瓶颈

Posted

技术标签:

【中文标题】Python中使用pandas数据框和嵌套for循环的基于项目的协作过滤器的瓶颈【英文标题】:Bottleneck in item-based collaborative filter using pandas data frames and nested for loops in Python 【发布时间】:2016-02-17 18:45:46 【问题描述】:

我有一个包含 100246 行和 7 列的输入数据集(csv 格式)。这是取自http://grouplens.org/datasets/movielens/ 的电影评分数据。我的数据框的头部是:

In [5]: df.head()
Out[5]: 
   movieId                                       genres  userId      rating  \
0        1  Adventure|Animation|Children|Comedy|Fantasy       1       5   
1        1  Adventure|Animation|Children|Comedy|Fantasy       2       3   
2        1  Adventure|Animation|Children|Comedy|Fantasy       5       4   
3        1  Adventure|Animation|Children|Comedy|Fantasy       6       4   
4        1  Adventure|Animation|Children|Comedy|Fantasy       8       3   

 imdbId       title  relDate  
0  114709  Toy Story      1995  
1  114709  Toy Story      1995  
2  114709  Toy Story      1995  
3  114709  Toy Story      1995  
4  114709  Toy Story      1995 

使用此数据集,我使用用户评分之间的欧几里德距离计算每对电影之间的相似度得分(即,如果两部电影被用户样本评分相似,则电影高度相关)。目前,这是通过遍历所有电影对并使用 if 语句仅查找包含当前感兴趣电影的那些对来执行的:

  for i,item in enumerate(df['movieId'].unique()):
      for j, item_comb in enumerate(combinations(df['movieId'].unique(),2)):
        if(item in item_comb ):
              ## calculate the similarity score between item i and the other item in item_comb

但是,鉴于数据集中有 8927 部不同的电影,对数约为 40M。这是一个主要瓶颈。所以我的问题是有哪些方法可以加快我的代码速度?

【问题讨论】:

如果您尝试生成某种矩阵,您不应该尝试将所有类型扩展为它们自己的列,然后使用1/0True/False 填充行,然后仅使用@987654326 @带有用户选择的过滤器来产生相似度计算? 相似度得分不是基于类型,而是两部电影的用户评分之间的相关性(或欧式距离)。所以对于两部电影,我有两个向量 (x,y) 代表用户给出的电影评分。我已经编辑了帖子以明确说明这一点。 有很多这样的欧几里得距离问题,大多数带有numpyscipy标签。 听起来像是一种机器学习算法,类似于协同过滤——也许你可以谷歌一下,找到一个可以为你高效完成这项工作的库 是的,没错。我正在使用基于项目的协作过滤器。我会看看我能在谷歌上挖掘什么。 【参考方案1】:

在此链接 (collaborative-filtering scalability) 中,看起来 MongoDB 可用于在超大型数据集上使用协作过滤器。

Spark(collaborative-filter with Apache Spark) 也可能合适。

【讨论】:

【参考方案2】:

有一些方法可以将迭代相似度计算转换为矩阵乘法。如果您使用余弦相似度,则转换在this stack exchange question 的答案中进行了更详细的说明。

另一种方法是使用 scikit-learn 包中的成对相似度指标,该包具有cosine similarity 的实现。

from scikit-learn.metrics.pairwise import cosine_similarity
user_ratings_df = ....            # create the user x item dataframe

# Note the dataframe is transposed to convert to items as rows 
item_similarity = cosine_similarity(user_ratings_df.T)

【讨论】:

【参考方案3】:

向量化比for循环更好。

可能有两个有用的 pandas 函数:pivot_table() 和 corr()

例如:

In [5]: pt = df.pivot_table(columns=['movieId'], index=['userId'], values='rating')
Out[5]: 
   movieId       1    2    3    4    5
   userId                           
         1       5   ...
         2       3   ...
         5       4   ...
         6       4   ...
         8       3   ...

In [6]: pt.corr()
Out[6]: 
   movieId       1    2    3    4    5
   movieId                           
         1       1.0     ...
         2       0.XXX   ...
         3       0.XXX   ...
         4       0.XXX   ...
         5       0.XXX   ...

请注意,这里的 corr() 计算的是电影之间的标准相关系数(皮尔逊相关),而不是欧几里得距离。您还可以使用参数 min_periods 设置每对列所需的最小观察次数以获得有效结果。

【讨论】:

【参考方案4】:

在这个paper 中,令人厌恶的是,您可能会用另一种方法使您的算法更快

在亚马逊的一篇论文(2003 年)中,他们已经描述了,我没记错。

总之,这个算法背后最重要的思想是以另一种方式计算两个向量的点积,而不是遍历每个向量 简单的元素。通过这种方式,该算法仅在具有相同客户的情况下计算两个项目。换句话说,它跳过 0 相似度 计算。

   For each item in product catalog, I1
      For each customer C who purchased I1
        For each item I2 purchased by customer C
          Record that a customer purchased I1 and I2
      For each item I2
        Compute the similarity between I1 and I2

【讨论】:

以上是关于Python中使用pandas数据框和嵌套for循环的基于项目的协作过滤器的瓶颈的主要内容,如果未能解决你的问题,请参考以下文章

使用 Pandas 在 Python 中过滤嵌套的 JSON 数据

Python中的嵌套循环

嵌套 for 循环的 Pandas 在创建的不同数据帧上插入多个数据

使用 Pandas 将数据框和其他数据保存在同一个 .csv 文件中

使用 pandas python 将嵌套的 JSON 解析为多个数据帧

Python中将列表元素嵌套到数据框