我不认为在这种情况下使用csr格式有什么优势。当然,所有非零值都被收集在一个.data数组中,并且相应的列索引在.indices中。但是它们以不同长度的块存在。这意味着它们无法并行处理或使用numpy数组步幅。
一种解决方案是将这些块填充到相同长度的块中。这就是.toarray()所做的。然后您可以使用argsort(axis=1)或argpartition找到最大值。
另一种方法是将它们分成按行大小的块,并处理每个块。这就是您使用.getrow的方式。另一种分割它们的方法是转换为lil格式,并处理.data和.rows数组的子列表。
可能的第三个选择是使用ufunc reduceat方法。这使您可以将ufunc reduction方法应用于数组的连续块。已经有了像np.add这样的已建立的ufunc可以利用这一点。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
格式是为了有效地进行乘法而开发的。
对于我当前的示例矩阵(为另一个 SO 稀疏问题创建),
<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
其他一切操作的执行时间都是1毫秒或更长。
我建议专注于开发您的单行函数,例如:
def max_n(row_data, row_indices, n):
i = row_data.argsort()[-n:]
top_values = row_data[i]
top_indices = row_indices[i]
return top_values, top_indices, i
然后看看它如何适用于这些迭代方法中的一个。
tolil()
看起来最有前途。
我还没有解决如何收集这些结果的问题。 它们应该是列表中的列表,具有 10 列的数组,每行具有 10 个值的另一个稀疏矩阵等吗?
按列索引排序大量稀疏行并保存前 K 个值的类似问题 - 几年前的类似问题,但无人回答。
scipy 稀疏矩阵每行或每列的 argmax - 最近提出的问题,寻求 csr 行的 argmax。 我讨论了一些相同的问题。
如何加快 numpy 中的循环? - 使用
np.frompyfunc
创建
ufunc
的示例。 我不知道生成的函数是否具有
.reduceat
方法。
增加稀疏矩阵中前 k 个元素的值 - 获取 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
这是一个相当不错的速度。但我想不出通过归约来实现argsort
(需要两个参数的二进制函数)的方法。因此,对于这个问题来说,这可能是一个死胡同。
lil
格式开始)。 - hpauljargpartion
比argsort
更快。但仍然有一个问题,那就是你是否可以比逐行迭代更好。lil
和csr
是最快的两种格式。 - hpaulj