【问题标题】:scipy.sparse : Set row to zerosscipy.sparse :将行设置为零
【发布时间】:2012-08-26 12:01:38
【问题描述】:

假设我有一个 CSR 格式的矩阵,将一行(或多行)设置为零的最有效方法是什么?

以下代码运行很慢:

A = A.tolil()
A[indices, :] = 0
A = A.tocsr()

我不得不转换为scipy.sparse.lil_matrix,因为 CSR 格式似乎既不支持花哨的索引也不支持为切片设置值。

【问题讨论】:

  • 好吧,我刚试过[A.__setitem__((i, j), 0) for i in indices for j in range(A.shape[1])]SciPy 告诉我SparseEfficiencyWarning: changing the sparsity structure of a csr_matrix is expensive. lil_matrix is more efficient....
  • 不知道 scipy 是否支持它,但由于它是一个 CSR 矩阵,因此可以有效地处理(至少手动)。一个问题是,你想改变稀疏模式,还是那些 0 应该只是数字 0?
  • 我不确定稀疏模式是什么意思。我继续使用 scipy.sparse.linalg.spsolve 函数求解方程组。我希望这确定了改变稀疏模式的必要性,或者缺乏。
  • @AshwinSrinath 我发布了一个答案,我猜你可能不关心它。如果您连接到相对较低级别的求解器,它可能可能很有趣,这就像为刚刚修改的每次迭代返回雅可比行列式一样,然后求解器可能期望稀疏模式不会改变。阅读维基百科文章,但我认为您应该更改它(以节省空间和计算)。

标签: row scipy sparse-matrix


【解决方案1】:

我猜 scipy 只是没有实现它,但 CSR 格式会很好地支持这一点,请阅读关于“稀疏矩阵”的维基百科文章,了解 indptr 等是什么:

# A.indptr is an array, one for each row (+1 for the nnz):

def csr_row_set_nz_to_val(csr, row, value=0):
    """Set all nonzero elements (elements currently in the sparsity pattern)
    to the given value. Useful to set to 0 mostly.
    """
    if not isinstance(csr, scipy.sparse.csr_matrix):
        raise ValueError('Matrix given must be of CSR format.')
    csr.data[csr.indptr[row]:csr.indptr[row+1]] = value

# Now you can just do:
for row in indices:
    csr_row_set_nz_to_val(A, row, 0)

# And to remove zeros from the sparsity pattern:
A.eliminate_zeros()

当然,这会从稀疏模式中删除从另一个位置使用eliminate_zeros 设置的 0。如果你想这样做(此时)取决于你真正在做什么,即。延迟消除可能是有意义的,直到所有其他可能添加新零的计算也完成,或者在某些情况下,您可能有 0 值,您想稍后再次更改,因此消除它们会非常糟糕!

您原则上当然可以将eliminate_zerosprune 短路,但这会很麻烦,而且可能会更慢(因为您不会在C 中这样做)。


关于 eliminiate_zeros(和修剪)的详细信息

稀疏矩阵通常不保存零元素,而只是存储非零元素所在的位置(大致和各种方法)。 eliminate_zeros 从稀疏模式中删除矩阵中的所有零(即,没有为该位置存储任何值,而之前 存储了一个值,但它是 0)。如果您想稍后将 0 更改为不同的值,则消除是不好的,否则会节省空间。

Prune 只会在存储的数据数组超过必要时缩小它们。请注意,虽然我第一次在里面有 A.prune(),但 A.eliminiate_zeros() 已经包含了 prune。

【讨论】:

  • 谢谢!这大大加快了速度!我只是想知道 remove_zeros 和 prune 语句在那里做什么?
  • 添加了一个(希望可以理解的)句子。注意prune() 是不必要的,eliminate_zeros 已经调用了prune
【解决方案2】:

您可以使用矩阵点积来实现归零。由于我们将使用的矩阵非常稀疏(我们要清零的行/列与零对角线),因此乘法应该是有效的。

您需要以下功能之一:

import scipy.sparse

def zero_rows(M, rows):
    diag = scipy.sparse.eye(M.shape[0]).tolil()
    for r in rows:
        diag[r, r] = 0
    return diag.dot(M)

def zero_columns(M, columns):
    diag = scipy.sparse.eye(M.shape[1]).tolil()
    for c in columns:
        diag[c, c] = 0
    return M.dot(diag)

使用示例:

>>> A = scipy.sparse.csr_matrix([[1,0,3,4], [5,6,0,8], [9,10,11,0]])
>>> A
<3x4 sparse matrix of type '<class 'numpy.int64'>'
        with 9 stored elements in Compressed Sparse Row format>
>>> A.toarray()
array([[ 1,  0,  3,  4],
       [ 5,  6,  0,  8],
       [ 9, 10, 11,  0]], dtype=int64)

>>> B = zero_rows(A, [1])
>>> B
<3x4 sparse matrix of type '<class 'numpy.float64'>'
        with 6 stored elements in Compressed Sparse Row format>
>>> B.toarray()
array([[  1.,   0.,   3.,   4.],
       [  0.,   0.,   0.,   0.],
       [  9.,  10.,  11.,   0.]])

>>> C = zero_columns(A, [1, 3])
>>> C
<3x4 sparse matrix of type '<class 'numpy.float64'>'
        with 5 stored elements in Compressed Sparse Row format>
>>> C.toarray()
array([[  1.,   0.,   3.,   0.],
       [  5.,   0.,   0.,   0.],
       [  9.,   0.,  11.,   0.]])

【讨论】:

    【解决方案3】:

    更新到最新版本的 scipy。它支持精美的索引。

    【讨论】:

      【解决方案4】:

      我想填写answer given by @seberg。如果要将 nnz 值设置为零,则应修改 CSR 矩阵的结构,而不仅仅是修改.data 属性。

      这段代码的当前行为是,

      >>> import scipy.sparse
      >>> import numpy as np
      >>> A = scipy.sparse.csr_matrix([[0,1,0], [2,0,3], [0,0,0], [4,0,0]])
      >>> A.toarray()
      array([[0, 1, 0],
             [2, 0, 3],
             [0, 0, 0],
             [4, 0, 0]], dtype=int64)
      >>> csr_row_set_nz_to_val(A, 1)
      >>> A.toarray()
      array([[0, 1, 0],
             [0, 0, 0],
             [0, 0, 0],
             [4, 0, 0]], dtype=int64)
      >>> A.data
      array([1, 0, 0, 4], dtype=int64)
      >>> A.indices
      array([1, 0, 2, 0], dtype=int32)
      >>> A.indptr
      array([0, 1, 3, 3, 4], dtype=int32)
      

      因为我们处理的是稀疏矩阵,所以我们不希望 A.data 数组中有零。我认为应该修改csr_row_set_nz_to_val如下

      def csr_row_set_nz_to_val(csr, row, value=0):
          """Set all nonzero elements of a CSR matrix M (elements currently in the sparsity pattern)
          to the given value. Useful to set to 0 mostly.
          """
          if not isinstance(csr, scipy.sparse.csr_matrix):
              raise ValueError("Matrix given must be of CSR format.")
          if value == 0:
              csr.data = np.delete(csr.data, range(csr.indptr[row], csr.indptr[row+1])) # drop nnz values
              csr.indices = np.delete(csr.indices, range(csr.indptr[row], csr.indptr[row+1])) # drop nnz column indices
              csr.indptr[(row+1):] = csr.indptr[(row+1):] - (csr.indptr[row+1] - csr.indptr[row])
          else:
              csr.data[csr.indptr[row]:csr.indptr[row+1]] = value # replace nnz values by another nnz value
      

      最后,我们会得到代替

      >>> A = scipy.sparse.csr_matrix([[0,1,0], [2,0,3], [0,0,0], [4,0,0]])
      >>> csr_row_set_nz_to_val(A, 1)
      >>> A.toarray()
      array([[0, 1, 0],
             [0, 0, 0],
             [0, 0, 0],
             [4, 0, 0]], dtype=int64)
      >>> A.data
      array([1, 4], dtype=int64)
      >>> A.indices
      array([1, 0], dtype=int32)
      >>> A.indptr
      array([0, 1, 1, 1, 2], dtype=int32)
      

      【讨论】:

        猜你喜欢
        • 2017-02-02
        • 2021-08-27
        • 1970-01-01
        • 2014-03-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-12
        相关资源
        最近更新 更多