在数据帧上执行操作的最佳方式是什么?对于每一行,我都需要在另一个数据帧上进行选择?
例如:
我的第一个数据帧具有每对到对项目之间的相似性。首先,我将假设每个相似性为零,然后计算正确的相似性。
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
中选择每个项目的评分向量,并计算这两者之间的余弦距离。
我想知道做这件事最不愚蠢的方法。以下是我迄今为止尝试的内容:
===第一次尝试-在行上迭代
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((。这是我迄今为止做得最好的一次:
===第二次尝试-索引.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.034961s才执行。比在行上迭代更慢。
因此,这是一次天真的矢量化尝试。有可能做到吗?我还有哪些其他选项可以改进运行时间?
感谢您的关注。
对于您给定的示例,我只想将其转换为一个数组,然后继续我的生活。
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采用稀疏数组,因此只要项目的数量合理(因为输出矩阵是密集的(,这应该是可以解决的。
相同但不同。使用numpy数组。对于小阵列来说很好,但对于10k行,您会有一些大阵列。
import numpy as np
data = rating_df.unstack().values # shape (4,5)
udotv = np.dot(data,data.T) # shape (4,4)
mag_data = np.linalg.norm(data,axis=1)
mag = mag_data * mag_data[:,None]
cos_sim = 1 - (udotv / mag)
df['sim2'] = cos_sim.flatten()
4k个用户和14k个项目几乎毁掉了我可怜的电脑。我将不得不看看sklearn.metrics.pairwise.cosine_similarity
是如何处理这么大的数据的。