如何矢量化包含自定义分组的熊猫计算?

问题描述:

我有一个的大数据帧控股用户映射(索引)项的计数(列):如何矢量化包含自定义分组的熊猫计算?

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]。这可以用于根据“类似”用户获得的项目向用户建议新项目。

这天真实现使用签名作为一个正则表达式来过滤出相关行和一个循环遍历所有签名:

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)签名上循环需要几个小时。

向量化循环应该会带来巨大的性能提升,但是我对熊猫的使用经验有限,所以我不确定如何去做。甚至可以矢量化这种类型的计算?也许使用口罩?

+0

为什么有这个例子只有4个签名?你是否需要其他组合,如.x ..,。xxx,x.xx等?为什么选择这4个? – Allen

+0

@Allen签名的集合只是来自users_items数据框中发生的任何数据。并非所有可能的签名都会发生。 –

+0

so'x ...'应该是所有行的总和,它是其签名的子集 –

取而代之的是string的签名,你可以使用一个frozenset

​​

另一种是

def create_signature(request_counts): 
    return frozenset(request_counts.replace({0: None}).dropna().index) 

我没有足够的大,看一个人是否是比速度更快的数据集其他。

如果有重复列,插入前.index

reset_index()电话本可以让你vectorise你的过滤器到底

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 4 
+0

我会认为使用frozensets(我想自己有点阵列)而不是字符串签名会快得多。不幸的是,生成冻结​​签名需要更长的时间(2分钟与10秒)。这就是说,使用fronzesets循环只需要6个小时而不是8个,所以这里有一个明显的改进。不过,我认为只有向量化for循环才能够加速这个操作。 –

+0

for循环仍然需要与字符串1一样长,因为我期望'set'比较比'regex'更快。位阵列可能会更快,我提交我的评论后,我做了 –

+0

。我现在更新了它。 –