【问题标题】:Set row of csr_matrix设置 csr_matrix 的行
【发布时间】:2019-08-08 20:16:36
【问题描述】:

我有一个稀疏的 csr_matrix,我想将单行的值更改为不同的值。但是,我找不到一个简单有效的实现。这就是它必须做的:

A = csr_matrix([[0, 1, 0],
                [1, 0, 1],
                [0, 1, 0]])
new_row = np.array([-1, -1, -1])
print(set_row_csr(A, 2, new_row).todense())

>>> [[ 0,  1, 0],
     [ 1,  0, 1],
     [-1, -1, -1]]

这是我当前的set_row_csr 实现:

def set_row_csr(A, row_idx, new_row):
    A[row_idx, :] = new_row
    return A

但这给了我一个SparseEfficiencyWarning。有没有办法在没有手动索引杂耍的情况下完成这项工作,或者这是我唯一的出路?

【问题讨论】:

    标签: python performance sparse-matrix


    【解决方案1】:

    physicalattraction 的答案确实要快得多。它比我的解决方案要快得多,我的解决方案只是添加一个带有该单行集的单独矩阵。虽然加法比切片法快。

    对我来说,在 csr_matrix 中设置行或在 csc_matrix 中设置列​​的最快方法是自己修改基础数据。

    def time_copy(A, num_tries = 10000):
        start = time.time()
        for i in range(num_tries):
            B = A.copy()
        end = time.time()
        return end - start
    
    def test_method(func, A, row_idx, new_row, num_tries = 10000):
        start = time.time()
        for i in range(num_tries):
            func(A.copy(), row_idx, new_row)
        end = time.time()
        copy_time = time_copy(A, num_tries)
        print("Duration {}".format((end - start) - copy_time))
    
    def set_row_csr_slice(A, row_idx, new_row):
        A[row_idx,:] = new_row
    
    def set_row_csr_addition(A, row_idx, new_row):
        indptr = np.zeros(A.shape[1] + 1)
        indptr[row_idx +1:] = A.shape[1]
        indices = np.arange(A.shape[1])
        A += csr_matrix((new_row, indices, indptr), shape=A.shape)
    
    >>> A = csr_matrix((np.ones(1000), (np.random.randint(0,1000,1000), np.random.randint(0, 1000, 1000))))
    >>> test_method(set_row_csr_slice, A, 200, np.ones(A.shape[1]), num_tries = 10000)
    Duration 4.938395977020264
    
    >>> test_method(set_row_csr_addition, A, 200, np.ones(A.shape[1]), num_tries = 10000)
    Duration 2.4161765575408936
    
    >>> test_method(set_row_csr, A, 200, np.ones(A.shape[1]), num_tries = 10000)
    Duration 0.8432261943817139
    

    随着矩阵的大小和稀疏性,切片解决方案的缩放比例也会变得更差。

    # Larger matrix, same fraction sparsity
    >>> A = csr_matrix((np.ones(10000), (np.random.randint(0,10000,10000), np.random.randint(0, 10000, 10000))))
    >>> test_method(set_row_csr_slice, A, 200, np.ones(A.shape[1]), num_tries = 10000)
    Duration 18.335174798965454
    
    >>> test_method(set_row_csr, A, 200, np.ones(A.shape[1]), num_tries = 10000)
    Duration 1.1089558601379395
    
    # Super sparse matrix
    >>> A = csr_matrix((np.ones(100), (np.random.randint(0,10000,100), np.random.randint(0, 10000, 100))))
    >>> test_method(set_row_csr_slice, A, 200, np.ones(A.shape[1]), num_tries = 10000)
    Duration 13.371600151062012
    
    >>> test_method(set_row_csr, A, 200, np.ones(A.shape[1]), num_tries = 10000)
    Duration 1.0454308986663818
    

    【讨论】:

      【解决方案2】:

      最后,我设法通过索引杂耍完成了这项工作。

      def set_row_csr(A, row_idx, new_row):
          '''
          Replace a row in a CSR sparse matrix A.
      
          Parameters
          ----------
          A: csr_matrix
              Matrix to change
          row_idx: int
              index of the row to be changed
          new_row: np.array
              list of new values for the row of A
      
          Returns
          -------
          None (the matrix A is changed in place)
      
          Prerequisites
          -------------
          The row index shall be smaller than the number of rows in A
          The number of elements in new row must be equal to the number of columns in matrix A
          '''
          assert sparse.isspmatrix_csr(A), 'A shall be a csr_matrix'
          assert row_idx < A.shape[0], \
                  'The row index ({0}) shall be smaller than the number of rows in A ({1})' \
                  .format(row_idx, A.shape[0])
          try:
              N_elements_new_row = len(new_row)
          except TypeError:
              msg = 'Argument new_row shall be a list or numpy array, is now a {0}'\
              .format(type(new_row))
              raise AssertionError(msg)
          N_cols = A.shape[1]
          assert N_cols == N_elements_new_row, \
                  'The number of elements in new row ({0}) must be equal to ' \
                  'the number of columns in matrix A ({1})' \
                  .format(N_elements_new_row, N_cols)
      
          idx_start_row = A.indptr[row_idx]
          idx_end_row = A.indptr[row_idx + 1]
          additional_nnz = N_cols - (idx_end_row - idx_start_row)
      
          A.data = np.r_[A.data[:idx_start_row], new_row, A.data[idx_end_row:]]
          A.indices = np.r_[A.indices[:idx_start_row], np.arange(N_cols), A.indices[idx_end_row:]]
          A.indptr = np.r_[A.indptr[:row_idx + 1], A.indptr[(row_idx + 1):] + additional_nnz]
      

      【讨论】:

      • 你有时间测试这个吗?有了这些np.r_ 电话,我想知道它是否最终能节省时间。
      • 老实说:如果我经过时间测试,我已经不记得了。现在已经是两年前了。
      【解决方案3】:

      这是我的方法:

      A = A.tolil()
      A[index, :] = new_row
      A = A.tocsr()
      

      只需转换为lil_matrix,更改行并转换回来。

      【讨论】:

        【解决方案4】:

        这个set_row_csr 有问题。是的,它速度很快,而且似乎适用于某些测试用例。但是,在我的测试用例中,它似乎混淆了 csr 稀疏矩阵的内部 csr 结构。之后尝试lil_matrix(A),您将看到错误消息。

        【讨论】:

        • 我创建了一个非常小的测试矩阵,使用set_row_csr 修改并使用lil_matrix(A) 进行转换。虽然没有出现错误...
        【解决方案5】:

        在物理吸引力的回答中,len(new_row) 必须等于 A.shape[1],这在添加稀疏行时可能并不有趣。

        因此,根据他的回答,我想出了一种方法来设置 csr 中的行,同时保留 sparcity 属性。此外,我添加了一种将密集数组转换为稀疏数组的方法(关于数据、索引格式)

        def to_sparse(dense_arr):
            sparse = [(data, index) for index, data in enumerate(dense_arr) if data != 0]
        
            # Convert list of tuples to lists
            sparse = list(map(list, zip(*sparse)))
        
            # Return data and indices
            return sparse[0], sparse[1]
        
        def set_row_csr_unbounded(A, row_idx, new_row_data, new_row_indices):
            '''
            Replace a row in a CSR sparse matrix A.
        
            Parameters
            ----------
            A: csr_matrix
                Matrix to change
            row_idx: int
                index of the row to be changed
            new_row_data: np.array
                list of new values for the row of A
            new_row_indices: np.array
                list of indices for new row
        
            Returns
            -------
            None (the matrix A is changed in place)
        
            Prerequisites
            -------------
            The row index shall be smaller than the number of rows in A
            Row data and row indices must have the same size
            '''
            assert isspmatrix_csr(A), 'A shall be a csr_matrix'
            assert row_idx < A.shape[0], \
                    'The row index ({0}) shall be smaller than the number of rows in A ({1})' \
                    .format(row_idx, A.shape[0])
        
            try:
                N_elements_new_row = len(new_row_data)
            except TypeError:
                msg = 'Argument new_row_data shall be a list or numpy array, is now a {0}'\
                .format(type(new_row_data))
                raise AssertionError(msg)
        
            try:
                assert N_elements_new_row == len(new_row_indices), \
                        'new_row_data and new_row_indices must have the same size'
            except TypeError:
                msg = 'Argument new_row_indices shall be a list or numpy array, is now a {0}'\
                .format(type(new_row_indices))
                raise AssertionError(msg)
        
            idx_start_row = A.indptr[row_idx]
            idx_end_row = A.indptr[row_idx + 1]
        
            A.data = np.r_[A.data[:idx_start_row], new_row_data, A.data[idx_end_row:]]
            A.indices = np.r_[A.indices[:idx_start_row], new_row_indices, A.indices[idx_end_row:]]
            A.indptr = np.r_[A.indptr[:row_idx + 1], A.indptr[(row_idx + 1):] + N_elements_new_row]
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-08-05
          • 1970-01-01
          • 2017-07-07
          • 2014-10-22
          • 2023-03-17
          • 1970-01-01
          • 2018-01-22
          • 2020-08-06
          相关资源
          最近更新 更多