在这种情况下,我看不出csr 格式的优势是什么。当然,所有非零值都收集在一个.data 数组中,对应的列索引位于.indices 中。但它们位于不同长度的块中。这意味着它们不能并行处理或使用numpy 数组步幅进行处理。
一种解决方案是将这些块填充到共同长度的块中。这就是.toarray() 所做的。然后你可以用argsort(axis=1) or withargpartition`找到最大值。
另一个是将它们分成行大小的块,并处理每个块。这就是您使用.getrow 所做的事情。另一种分解它们的方法是转换为lil 格式,并处理.data 和.rows 数组的子列表。
第三种可能的选择是使用ufunc reduceat 方法。这使您可以将 ufunc reduction 方法应用于数组的顺序块。有已建立的ufunc,如np.add,利用了这一点。 argsort 不是这样的功能。但是有一种方法可以从 Python 函数构造 ufunc,并且比常规 Python 迭代获得一些适度的速度。 [我需要查找一个最近的 SO 问题来说明这一点。]
我将用一个更简单的函数来说明其中的一些,即对行求和。
如果A2是一个csr矩阵。
A2.sum(axis=1) # the fastest compile csr method
A2.A.sum(axis=1) # same, but with a dense intermediary
[np.sum(l.data) for l in A2] # iterate over the rows of A2
[np.sum(A2.getrow(i).data) for i in range(A2.shape[0])] # iterate with index
[np.sum(l) for l in A2.tolil().data] # sum the sublists of lil format
np.add.reduceat(A2.data, A2.indptr[:-1]) # with reduceat
A2.sum(axis=1) 实现为矩阵乘法。这与排序问题无关,但仍然是看待求和问题的一种有趣方式。请记住,csr 格式是为高效乘法而开发的。
对于我当前的示例矩阵(为另一个如此稀疏的问题创建)
<8x47752 sparse matrix of type '<class 'numpy.float32'>'
with 32 stored elements in Compressed Sparse Row format>
一些比较时间是
In [694]: timeit np.add.reduceat(A2.data, A2.indptr[:-1])
100000 loops, best of 3: 7.41 µs per loop
In [695]: timeit A2.sum(axis=1)
10000 loops, best of 3: 71.6 µs per loop
In [696]: timeit [np.sum(l) for l in A2.tolil().data]
1000 loops, best of 3: 280 µs per loop
其他一切都是 1ms 或更多。
我建议专注于开发单行函数,例如:
def max_n(row_data, row_indices, n):
i = row_data.argsort()[-n:]
# i = row_data.argpartition(-n)[-n:]
top_values = row_data[i]
top_indices = row_indices[i] # do the sparse indices matter?
return top_values, top_indices, i
然后看看 if 如何适合这些迭代方法之一。 tolil() 看起来最有前途。
我还没有解决如何收集这些结果的问题。它们应该是列表列表、10 列数组、另一个每行 10 个值的稀疏矩阵等吗?
sorting each row of a large sparse & saving top K values & column index - 几年前的类似问题,但没有答案。
Argmax of each row or column in scipy sparse matrix - 最近的问题正在寻找 argmax 以获取 csr 的行。我讨论了一些相同的问题。
how to speed up loop in numpy? - 如何使用np.frompyfunc 创建ufunc 的示例。不知道生成的函数有没有.reduceat方法。
Increasing value of top k elements in sparse matrix - 获取 csr 的前 k 个元素(不是按行)。 argpartition 的案例。
用np.frompyfunc实现的行求和:
In [741]: def foo(a,b):
return a+b
In [742]: vfoo=np.frompyfunc(foo,2,1)
In [743]: timeit vfoo.reduceat(A2.data,A2.indptr[:-1],dtype=object).astype(float)
10000 loops, best of 3: 26.2 µs per loop
这是可观的速度。但我想不出一种编写二进制函数(带 2 个参数)的方法,它可以通过归约实现 argsort。所以这可能是这个问题的死胡同。