我有一个很大的数据框架,它将用户(索引)映射到项目(列)的计数:
users_items = pd.DataFrame(np.array([[0, 1, 1, 0], # user 0
[1, 0, 0, 0], # user 1
[5, 0, 0, 9], # user 2
[0, 3, 5, 0], # user 3
[0, 2, 2, 0], # user 4
[7, 0, 0, 1], # user 5
[3, 5, 0, 4]]), # user 6
columns=list('ABCD'))对于每个用户,我希望找到至少具有相同项目的非零计数的所有用户,并对其计数求和。因此,对于用户1,这将是用户1、2、5和6,并且计数的和等于[16, 5, 0, 14]。这可以用来根据“相似”用户获得的项目向用户推荐新项目。
这个简单的实现使用一个签名作为正则表达式来过滤出相关的行,并使用一个for循环遍历所有签名:
def create_signature(request_counts):
return ''.join('x' if count else '.' for count in request_counts)
users_items['signature'] = users_items.apply(create_signature, axis=1).astype('category')
current_items = users_items.groupby('signature').sum()
similar_items = pd.DataFrame(index=current_items.index,
columns=current_items.columns)
for signature in current_items.index:
row = current_items.filter(regex=signature, axis='index').sum()
similar_items.loc[signature] = row结果是:
A B C D
signature
.xx. 0 6 8 0
x... 16 5 0 14
x..x 15 5 0 14
xx.x 3 5 0 4这可以很好地工作,但对于包含100k用户和大约600个项目的实际数据集来说,它太慢了。生成签名只需要10秒,但循环所有(40k)签名需要几个小时。
向量化循环应该会提供一个巨大的性能提升,但我使用Pandas的经验有限,所以我不确定如何去做。甚至有可能将这种类型的计算向量化?也许是用口罩?
发布于 2017-05-08 22:56:31
您可以使用frozenset而不是string作为签名
def create_signature(request_counts):
return frozenset(request_counts[request_counts != 0].index)另一种选择是
def create_signature(request_counts):
return frozenset(request_counts.replace({0: None}).dropna().index)我没有足够大的数据集来查看其中一个是否比另一个更快。
如果有重复的列,请在.index之前插入对reset_index()的调用
这使您可以在最后对过滤器进行矢量化
for signature in current_items.index:
row = current_items[signature <= current_items.index].sum()
similar_items.loc[signature] = row结果:
signature A B C D
frozenset({'B', 'C'}) 0 6 8 0
frozenset({'A'}) 16 5 0 14
frozenset({'A', 'D'}) 15 5 0 14
frozenset({'B', 'A', 'D'}) 3 5 0 4https://stackoverflow.com/questions/43845672
复制相似问题