为什么 scipy.take 不适用于稀疏矩阵?

Why does scipy.take not work with a sparse matrix?

如果我对 numpy.zeros 矩阵执行相同的操作,它就会起作用。但是对于 scipy 稀疏矩阵,它不起作用。为什么?

import scipy.sparse as sparse
import scipy as sp
a = sparse.lil_matrix((3,3), dtype=int)
a[0,0] = 0
a[1,1] = 1
a[2,2] = 2
b = a.sum(0)
bo = (-b).argsort()
ao = sp.take(a, bo, axis=1)

我收到错误:

ValueError: axis(=1) out of bounds

为什么那不起作用。有人可以告诉我如何解决吗?还是 scipy 稀疏矩阵不可能?

spicy.take 似乎没有实现稀疏矩阵的逻辑。但是,take 函数的大部分功能都可以使用花哨的索引来实现。就您而言,我相信这可以满足您的需求:

ao = a[:, bo.flat]

此处,bo.flat 是将 bo 矩阵转换为简单迭代器的简单方法。

您不能期望 scipy.sparse 矩阵的行为与密集的 numpy 矩阵或数组完全相同。

一方面,scipy.sparse 矩阵仅支持可应用于密集 arrays/matrices 的索引操作的子集,并且该子集取决于所讨论的特定稀疏格式。例如,您不能将切片索引应用于 coo_matrix,您只能在 dok_matrix 的单个轴上应用花式索引等。See here 对这些限制进行更多讨论。

在您的特定情况下,您可以使用花式索引代替 np.take:

ao = a[:, np.ravel(bo)]
# or ao = a[:, bo.flat]
# or ao = a[:, bo.A1]

然而,并非所有稀疏矩阵格式都支持这种索引,这可能解释了为什么稀疏矩阵缺少 .take 方法并且与 numpy.take 不兼容。

稍微扩展一下其他答案。 np.takesp.take 是一回事):

try:
    take = a.take
except AttributeError:
    return _wrapit(a, 'take', indices, axis, out, mode)
return take(indices, axis, out, mode)

换句话说,它试图执行

a.take(bo, axis=1)

但是a.takereturns一个属性错误。 a,稀疏矩阵没有take方法。并尝试将 a 转换为具有 np.array(a) 的数组也不起作用 - 它只是将稀疏矩阵包装在 0d 对象数组中。这就是我们得到 index 越界错误的原因。

使用 lil 格式,矩阵存储为两个列表,或者更具体地说是列表的对象数组,每行一个子列表。

In [620]: a.data
Out[620]: array([[1], [2], [3]], dtype=object)

In [621]: a.rows
Out[621]: array([[0], [1], [2]], dtype=object)

a.__getitem__ 是执行索引的函数。它将那种花哨的 numpy 之类的索引转换为列表理解。与数组相比,没有快速编译索引。

请注意,如果我首先将 a 转换为密集数组,则重新排序的速度会快得多。连往返都更快。

In [626]: b0=(-b.A1).argsort()
In [627]: timeit a[:,b0].A
1000 loops, best of 3: 705 us per loop

In [628]: timeit a.A[:,b0]
10000 loops, best of 3: 22.3 us per loop

In [630]: timeit sparse.lil_matrix(a.A[:,b0])
1000 loops, best of 3: 483 us per loop

因此,如果内存允许,将稀疏矩阵转换为数组、索引并以其他方式对其进行操作,然后再转换回稀疏矩阵可能会更有效。