首页 > 解决方案 > 在我需要对另一个数据帧进行子集化的数据帧上进行矢量化操作(皮尔逊相关性)

问题描述

对数据框进行操作的最佳方法是什么,对于每一行,我需要对另一个数据框进行选择?

例如:

我的第一个数据框具有每对项目之间的相似性。对于初学者,我将假设每个相似度为零,然后计算正确的相似度。

import pandas as pd
import numpy as np
import scipy as sp
from scipy.spatial import distance

 

items = [1,2,3,4]
item_item_idx = pd.MultiIndex.from_product([items, items], names = ['from_item', 'to_item'])
item_item_df = pd.DataFrame({'similarity': np.zeros(len(item_item_idx))}, 
                            index = item_item_idx
                            )

我的下一个数据框包含每个用户对每个项目的评分。为了简化起见,我们假设每个用户都对每个项目进行评分,并生成 1 到 5 之间的随机评分。

users = [1,2,3,4,5]

ratings_idx = pd.MultiIndex.from_product([items, users], names = ['item', 'user'])
rating_df = pd.DataFrame(
    {'rating': np.random.randint(low = 1, high =  6, size = len(users)*len(items))},
    columns = ['rating'],
    index = ratings_idx
    )

现在我有了评分,我想更新项目之间的余弦相似度。我需要做的是,对于 中的每一行item_item_df,从rating_df每个项目的评级向量中选择,并计算这两者之间的余弦距离。

我想知道最不愚蠢的方法来做到这一点。这是我到目前为止所尝试的:

==== FIRST TRY - 遍历行

def similarity(ii, iu):
    
    for index, row in ii.iterrows():
        
        v = iu.loc[index[0]]
        u = iu.loc[index[1]]
        
        row['similarity'] = distance.cosine(v, u)
    
    return(ii)


import time

start_time = time.time()
item_item_df = similarity(item_item_df, rating_df)
print('Time: {:f}s'.format(time.time() - start_time))

我花了 0.01002 秒来运行它。对于 10k 个项目的问题,我估计它需要 20 个小时才能运行。不好。

问题是,我正在遍历行,我希望我可以对其进行矢量化以使其更快。我玩弄了 df.apply() 和 df.map()。这是迄今为止我做的最好的:

==== 第二次尝试 - index.map()

def similarity_map(idx):
    
    v = rating_df.loc[idx[0]]
    u = rating_df.loc[idx[1]]
    
    return distance.cosine(v, u)

start_time = time.time()
item_item_df['similarity'] = item_item_df.index.map(similarity_map)
print('Time: {:f}s'.format(time.time() - start_time))

我花了 0.034961 秒来执行。比仅遍历行要慢。

所以这是一个幼稚的矢量化尝试。甚至有可能做到吗?我还有哪些其他选择可以改善运行时?

感谢您的关注。

标签: pythonpandassubsetvectorizationpearson-correlation

解决方案


对于您给定的示例,我只需将其转换为数组并继续我的生活。

from sklearn.metrics.pairwise import cosine_similarity

rating_df = rating_df.reset_index().pivot(index='item', columns='user')
cs_df = pd.DataFrame(cosine_similarity(rating_df),
                     index=rating_df.index, columns=rating_df.index)

>>> cs_df
item         1         2         3         4
item                                        
1     1.000000  0.877346  0.660529  0.837611
2     0.877346  1.000000  0.608781  0.852029
3     0.660529  0.608781  1.000000  0.758098
4     0.837611  0.852029  0.758098  1.000000

对于一个巨大的、高度稀疏的数组,这将更加困难。Sklearn cosine_similarity 采用稀疏数组,只要您的项目数量合理(因为输出矩阵将是密集的),这应该是可以解决的。


推荐阅读