代码之家  ›  专栏  ›  技术社区  ›  Jan

为什么scipy.sparse.csc_matrix.sum()的结果将其类型更改为numpy matrix?

  •  3
  • Jan  · 技术社区  · 6 年前

    我想生成一个大的稀疏矩阵并求和,但是我遇到了 MemoryError 很多。所以我试着通过 scipy.sparse.csc_matrix.sum 但是发现数据类型改回了numpy matrix 拿了钱之后。

    window = 10    
    np.random.seed = 0
    mat = sparse.csc_matrix(np.random.rand(100, 120)>0.5, dtype='d')
    print type(mat)
    >>> <class 'scipy.sparse.csc.csc_matrix'>
    
    mat_head = mat[:,0:window].sum(axis=1)
    print type(mat_head)
    >>> <class 'numpy.matrixlib.defmatrix.matrix'>
    

    所以我产生了 mat 作为零矩阵测试结果时 mat_head 都是零。

    mat = sparse.csc_matrix((100,120))
    print type(mat)
    >>> <class 'scipy.sparse.csc.csc_matrix'>
    mat_head = mat.sum(axis=1)
    print type(mat_head)
    >>> <class 'numpy.matrixlib.defmatrix.matrix'>
    print np.count_nonzero(mat_head)
    >>> 0
    

    为什么会这样?所以通过 scipy.sparse 对保存内存没有好处 numpy 当他们改变数据类型时?

    3 回复  |  直到 6 年前
        1
  •  3
  •   Andras Deak -- Слава Україні    6 年前

    我知道你的问题“为什么”主要是针对设计决策背后的动机,但是无论如何我追踪到了 csc_matrix.sum(axis=1) 实际上变成了一个核 matrix .

    这个 csc_matrix class 继承自 _cs_matrix class 继承自 _data_matrix class 继承自 spmatrix base class . 最后一个 implements .sum(ax) as

    if axis == 0:
        # sum over columns
        ret = np.asmatrix(np.ones(
            (1, m), dtype=res_dtype)) * self
    else:
        # sum over rows
        ret = self * np.asmatrix(np.ones((n, 1), dtype=res_dtype))
    

    换句话说, as also noted in a comment ,列/行和分别通过与1的密集行或列矩阵相乘来计算。此操作的结果将是一个密集矩阵,您可以在输出中看到它。

    而有些子类重写了 .sum() 方法,据我所知,这只发生在 axis=None case,所以您看到的结果可以归因于上面的代码块。

        2
  •  5
  •   Paul Panzer    6 年前

    尽可能给出一个很难理解的理由来说明什么是设计选择,我将提出以下论点:

    csr和csc格式是为稀疏但不是非常稀疏的矩阵设计的。特别是,对于具有明显少于n个非零的nxn矩阵,这些格式相当浪费,因为它们在数据和索引的顶部带有大小为n+1的字段indptr(描绘行或列)。

    因此,假设使用了适当的csc或csr矩阵,则可以合理地期望行或列的和不是稀疏的,并且相应的方法应该返回一个密集向量。

        3
  •  1
  •   hpaulj    6 年前

    这个 csr csc 为线性代数开发了格式,特别是大型稀疏线性方程组的求解

    A*x = b
    x = b/A
    

    A 必须是可逆的,并且不能具有0的所有行或列。

    A.sum(1) 是通过矩阵乘法,用1的(n,1)数组来完成的。

    与你 mat :

    In [203]: np.allclose(mat*np.mat(np.ones((120,1))), mat.sum(1))
    Out[203]: True
    

    我自己做这件事其实要快一点(在某个地方的开销?)

    In [204]: timeit mat.sum(1)
    92.7 µs ± 111 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    In [205]: timeit mat*np.mat(np.ones((120,1)))
    59.2 µs ± 53.1 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    

    我也可以用稀疏矩阵来实现这一点:

    In [209]: mat*sparse.csc_matrix(np.ones((120,1)))
    Out[209]: 
    <100x1 sparse matrix of type '<class 'numpy.float64'>'
        with 100 stored elements in Compressed Sparse Column format>
    In [211]: np.allclose(mat.sum(1),_.todense())
    Out[211]: True
    

    但是时间比较慢,即使我将稀疏创建移到循环之外:

    In [213]: %%timeit I=sparse.csc_matrix(np.ones((120,1)))
         ...: mat*I
         ...: 
    215 µs ± 401 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    

    如果 垫子 (115100,10) 对于所有0行中的许多行,这种全稀疏方法可以节省时间和空间。


    mat[:,:10] 也可通过矩阵乘法和稀疏抽取矩阵来执行。

    它实际上比行和慢:

    In [247]: timeit mat[:,:10]
    305 µs ± 10.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    In [248]: timeit mat[:,:10].sum(1)
    384 µs ± 9.05 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    

    我可以使用以下方法将列选择与求和结合起来:

    In [252]: I = sparse.lil_matrix((120,1),dtype=int); I[:10,:]=1; I=I.tocsc()
    In [253]: I
    Out[253]: 
    <120x1 sparse matrix of type '<class 'numpy.int64'>'
        with 10 stored elements in Compressed Sparse Column format>
    In [254]: np.allclose((mat*I).todense(),mat[:,:10].sum(1))
    Out[254]: True
    

    时间安排 mat*I 比较慢,不过我可以改进 I 施工步骤。

    I = sparse.csc_matrix((np.ones(10,int), np.arange(10), np.array([0,10])), shape=(120,1))