创建scipy.Lil_matrix有效地使用python生成器



我有一个生成相同长度的单维numpy.array s的生成器。我想要一个包含这些数据的稀疏矩阵。行是按照我想让它们在最终矩阵中的顺序生成的。csr矩阵优于lil矩阵,但我认为后者在我描述的场景中更容易构建。

假设row_gen是生成numpy.array行的生成器,下面的代码按预期工作。

def row_gen():
    yield numpy.array([1, 2, 3])
    yield numpy.array([1, 0, 1])
    yield numpy.array([1, 0, 0])
matrix = scipy.sparse.lil_matrix(list(row_gen()))

因为这个列表基本上会破坏生成器的任何优点,所以我希望下面的代码具有相同的最终结果。更具体地说,我不能在内存中保存整个密集矩阵(或所有矩阵行列表):

def row_gen():
    yield numpy.array([1, 2, 3])
    yield numpy.array([1, 0, 1])
    yield numpy.array([1, 0, 0])
matrix = scipy.sparse.lil_matrix(row_gen())

但是在运行时会引发以下异常:

TypeError: no supported conversion for types: (dtype('O'),)

我还注意到跟踪包括以下内容:

File "/usr/local/lib/python2.7/site-packages/scipy/sparse/lil.py", line 122, in __init__
  A = csr_matrix(A, dtype=dtype).tolil()

这让我认为使用scipy.sparse.lil_matrix最终将创建一个csr矩阵,然后将其转换为lil矩阵。在这种情况下,我宁愿只创建csr矩阵开始。

回顾一下,我的问题是:从python生成器或numpy单维数组创建scipy.sparse矩阵的最有效方法是什么?

让我们看一下sparse.lil_matrix的代码。它检查第一个参数:

if isspmatrix(arg1):    # is is already a sparse matrix
     ...
elif isinstance(arg1,tuple):    # is it the shape tuple
    if isshape(arg1):
        if shape is not None:
            raise ValueError('invalid use of shape parameter')
        M, N = arg1
        self.shape = (M,N)
        self.rows = np.empty((M,), dtype=object)
        self.data = np.empty((M,), dtype=object)
        for i in range(M):
            self.rows[i] = []
            self.data[i] = []
    else:
        raise TypeError('unrecognized lil_matrix constructor usage')
else:
    # assume A is dense
    try:
        A = np.asmatrix(arg1)
    except TypeError:
        raise TypeError('unsupported matrix type')
    else:
        from .csr import csr_matrix
        A = csr_matrix(A, dtype=dtype).tolil()
        self.shape = A.shape
        self.dtype = A.dtype
        self.rows = A.rows
        self.data = A.data

根据文档-您可以从另一个稀疏矩阵,形状和密集数组构造它。密集数组构造函数首先生成csr矩阵,然后将其转换为lil矩阵。

形状版本用如下数据构造一个空的lil:

In [161]: M=sparse.lil_matrix((3,5),dtype=int)
In [163]: M.data
Out[163]: array([[], [], []], dtype=object)
In [164]: M.rows
Out[164]: array([[], [], []], dtype=object)

传递一个生成器显然是行不通的——它不是一个密集数组。

但是创建了lil矩阵后,您可以使用常规数组赋值填充元素:

In [167]: M[0,:]=[1,0,2,0,0]
In [168]: M[1,:]=[0,0,2,0,0]
In [169]: M[2,3:]=[1,1]
In [170]: M.data
Out[170]: array([[1, 2], [2], [1, 1]], dtype=object)
In [171]: M.rows
Out[171]: array([[0, 2], [2], [3, 4]], dtype=object)
In [172]: M.A
Out[172]: 
array([[1, 0, 2, 0, 0],
       [0, 0, 2, 0, 0],
       [0, 0, 0, 1, 1]])

,你可以直接给子列表赋值(我认为这更快,但更危险):

In [173]: M.data[1]=[1,2,3]
In [174]: M.rows[1]=[0,2,4]
In [176]: M.A
Out[176]: 
array([[1, 0, 2, 0, 0],
       [1, 0, 2, 0, 3],
       [0, 0, 0, 1, 1]])

另一种增量的方法是先构造3个coo格式的数组或列表,然后再从中构造一个coocsr

sparse.bmat是另一个选项,它的代码是构建coo输入的一个很好的例子。我让你自己看看