【问题标题】:Multiply slice of scipy sparse matrix without changing sparsity在不改变稀疏度的情况下乘以 scipy 稀疏矩阵的切片
【发布时间】:2018-06-18 15:16:33
【问题描述】:

scipy 中,当我将稀疏矩阵的切片与仅包含零的数组相乘时,结果是一个比以前更少或相同稀疏的矩阵,即使它应该更多或相同稀疏。将矩阵的某些部分设置为 0 或 False 也是如此:

>>> import numpy as np
>>> from scipy.sparse import csr_matrix as csr
>>> M = csr(np.random.random((8,8))>0.9)
>>> M
<8x8 sparse matrix of type '<type 'numpy.bool_'>'
        with 6 stored elements in Compressed Sparse Row format>
>>> M[:,0] = False
>>> M
<8x8 sparse matrix of type '<type 'numpy.bool_'>'
        with 12 stored elements in Compressed Sparse Row format>
>>> M[:,0].multiply(np.array([[False] for i in xrange(8)]))
>>> M
<8x8 sparse matrix of type '<type 'numpy.bool_'>'
        with 12 stored elements in Compressed Sparse Row format>

这对于大型矩阵来说实际上计算量很大,因为它会迭代切片中的所有单元格,而不仅仅是非零单元格。

从数学/逻辑的角度来看,当乘以稀疏矩阵或向量时,所有空单元格肯定会保持为空 0*x == 0。设置为零也是如此:零单元不需要显式设置为零。

解决这个问题的最佳方法是什么?


我正在使用 scipy 版本 0.17.0

【问题讨论】:

  • 您能否详细说明“不能”部分,因为对我来说,它可以在不将第一个 col 设置为全零的情况下工作。
  • 那么,两个输入都是bool dtype,第一个有很多列,而第二个只有一个列?
  • 在这个例子中,是的。

标签: python numpy matrix scipy sparse-matrix


【解决方案1】:

要重新创建您的代码(使用int 类型以获得更紧凑的显示):

In [16]: M = sparse.csr_matrix(np.random.random((8,8))>.7).astype(int)
In [17]: M
Out[17]: 
<8x8 sparse matrix of type '<class 'numpy.int32'>'
    with 17 stored elements in Compressed Sparse Row format>
In [18]: M.A
Out[18]: 
array([[0, 0, 1, 0, 1, 0, 0, 0],
       [1, 0, 0, 0, 0, 0, 1, 0],
       [0, 0, 0, 0, 0, 0, 0, 1],
       [0, 0, 1, 0, 0, 1, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0],
       [1, 0, 0, 0, 0, 0, 0, 1],
       [1, 0, 0, 0, 0, 0, 1, 0],
       [1, 0, 1, 1, 1, 1, 0, 1]])
In [19]: M.tolil().data       # show nonzero values by row
Out[19]: 
array([list([1, 1]), list([1, 1]), list([1]), list([1, 1]), list([]),
       list([1, 1]), list([1, 1]), list([1, 1, 1, 1, 1, 1])], dtype=object)

显式设置行(或列)。注意效率警告:

In [20]: M[0,:] = 0
/usr/local/lib/python3.5/dist-packages/scipy/sparse/compressed.py:774: SparseEfficiencyWarning: Changing the sparsity structure of a csr_matrix is expensive. lil_matrix is more efficient.
  SparseEfficiencyWarning)
In [21]: M.tolil().data
Out[21]: 
array([list([0, 0, 0, 0, 0, 0, 0, 0]), list([1, 1]), list([1]),
       list([1, 1]), list([]), list([1, 1]), list([1, 1]),
       list([1, 1, 1, 1, 1, 1])], dtype=object)

所以是的,它已将行中的所有值设置为指定值。并且它不会尝试区分设置 0 和 1。可以看到M.__setitem__M._set_many中使用的代码(这是产生效率警告的地方)。

正如@jakevpd 所示,您需要明确告诉它消除多余的 0。它不会在分配期间尝试这样做。

In [22]: M.eliminate_zeros()
In [23]: M.tolil().data
Out[23]: 
array([list([]), list([1, 1]), list([1]), list([1, 1]), list([]),
       list([1, 1]), list([1, 1]), list([1, 1, 1, 1, 1, 1])], dtype=object)

通常不鼓励明确设置矩阵的值,尤其是csrcoo 甚至不允许这样做。如果您需要,推荐使用lil 格式。

In [24]: Ml = M.tolil()
In [25]: Ml[1,:] = 0
In [26]: Ml.data
Out[26]: 
array([list([]), list([]), list([1]), list([1, 1]), list([]), list([1, 1]),
       list([1, 1]), list([1, 1, 1, 1, 1, 1])], dtype=object)

lil 确实注意消除 0。

一行乘以一个 0 的数组不会改变稀疏度。它也不会就地执行。它产生一个新矩阵:

In [29]: M[1,:].multiply(np.zeros((1,8)))
Out[29]: 
<1x8 sparse matrix of type '<class 'numpy.float64'>'
    with 2 stored elements in COOrdinate format>
In [30]: _.A
Out[30]: array([[ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.]])
In [31]: M[1,:].A
Out[31]: array([[1, 0, 0, 0, 0, 0, 1, 0]], dtype=int32)

与稀疏矩阵相乘确实消除了 0(同样,不是原地):

In [32]: M[1,:].multiply(sparse.csr_matrix(np.zeros((1,8))))
Out[32]: 
<1x8 sparse matrix of type '<class 'numpy.float64'>'
    with 0 stored elements in Compressed Sparse Row format>

(请注意Out[29]Out[32] 之间的格式不同。)

作为一般规则,乘法,无论是元素还是矩阵,都是csr 矩阵最有效的运算,特别是如果other 也是稀疏的。事实上,行/列求和是通过矩阵乘法执行的,advanced 索引也是如此。

【讨论】:

    【解决方案2】:

    在处理稀疏矩阵时,更改稀疏模式通常是一项非常昂贵的操作,因此 scipy 不会默默地这样做。

    如果您想从稀疏矩阵中删除显式存储的零,您应该使用eliminate_zeros() 方法;例如:

    >>> M = csr(np.random.random((1000,1000))>0.9, dtype=float)
    >>> M
    <1000x1000 sparse matrix of type '<class 'numpy.float64'>'
        with 99740 stored elements in Compressed Sparse Row format>
    
    >>> M[:, 0] *= 0
    >>> M
    <1000x1000 sparse matrix of type '<class 'numpy.float64'>'
        with 99740 stored elements in Compressed Sparse Row format>
    
    >>> M.eliminate_zeros()
    >>> M
    <1000x1000 sparse matrix of type '<class 'numpy.float64'>'
        with 99657 stored elements in Compressed Sparse Row format>
    

    Scipy可以在执行此类操作后自动调用eliminate_zeros 例程,但开发人员在执行与更改稀疏结构这样昂贵的操作时选择为用户提供更大的灵活性和控制权。

    【讨论】:

    • 不删除空单元格是一回事。我知道这并不是在任何时候都自动完成的。主要问题是空单元格变成非空零单元格。
    • 这里也有一个权衡:scipy 可以隐式忽略用户明确设置为零的项目,但这会导致其他问题。例如在scipy.sparse.csgraph 中,显式零用于表示权重为零的图边。如果您删除了用户设置这些的能力,那么您的包就会变得不那么有用。正如python之禅所说,显式胜于隐式。
    • 好吧,在我看来,稀疏矩阵应该定义一个默认值,即False0Noneinf-inf。在您的示例中,它将是None,而 0 实际上具有不同的含义,在我看来,这大大扩展了稀疏性的定义......
    猜你喜欢
    • 2011-11-28
    • 2017-07-21
    • 2019-02-12
    • 2023-03-03
    • 2019-10-04
    • 1970-01-01
    • 2017-03-26
    • 2017-03-31
    • 1970-01-01
    相关资源
    最近更新 更多