进行csr_matrix加法时如何计算索引?

时间:2018-12-26 07:11:29

标签: python numpy scipy sparse-matrix

我正在学习稀疏矩阵运算的密码。但是,我对执行加法运算时如何计算csr_matrix索引感到困惑。我的代码很简单:

B = A + A.T
A.toarray()
array([[1., 1., 1., 1., 0., 1., 1., 0., 1., 1., 1., 1.],
   [0., 1., 1., 1., 0., 1., 1., 1., 1., 1., 1., 1.],
   [0., 1., 1., 1., 0., 1., 1., 1., 1., 1., 1., 1.],
   [0., 1., 1., 1., 0., 1., 1., 1., 1., 1., 1., 1.],
   [0., 1., 0., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
   [0., 1., 1., 1., 0., 1., 1., 1., 1., 1., 1., 1.],
   [0., 1., 1., 1., 0., 1., 1., 1., 1., 1., 1., 1.],
   [0., 1., 1., 1., 0., 1., 1., 1., 1., 1., 1., 1.],
   [0., 1., 1., 1., 0., 1., 1., 1., 1., 1., 1., 1.],
   [0., 1., 1., 1., 0., 1., 1., 1., 1., 1., 1., 1.],
   [0., 1., 1., 1., 0., 1., 1., 1., 1., 1., 1., 1.],
   [0., 1., 1., 1., 0., 1., 1., 1., 1., 1., 1., 1.]])
A.indices:
array([ 0,  2,  1,  5, 10,  3,  6, 11,  9,  8,  
    1,  2,  9, 10,  5,  8,  6, 11,  3,  7,  
    2, 10,  1,  5,  9,  6, 11,  7,  8,  3,  
    3,  8,  6,  7,  5, 11,  9, 10,  2,  1,  
    4,  9,  1,  7,  8, 11,  3,  6, 10,  5,  
    5,  6, 11, 10,  8,  9,  3,  2,  7,  1,  
    6,  5,  8, 11,  9, 10,  3,  7,  2,  1,  
    7,  9,  3,  6,  8,  5, 11, 10,  2,  1,  
    8,  6, 11,  9,  5,  3, 10,  7,  1,  2,  
    9, 11, 10,  8,  6,  5,  7,  3,  2,  1, 
    10, 11, 5,  9,  6,  8,  2,  3,  1,  7, 
    11, 10,  5,  6,  8,  9,  3,  7,  2, 1], dtype=int32)

我调试了进入scipy的代码以获取更多详细信息,然后发现.T操作将矩阵A转换为CSC格式矩阵。然后,除过载操作外,A.T将再次转换为CSR格式矩阵,但其索引更改如下:

A.T.toarray():
array([[1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
   [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
   [1., 1., 1., 1., 0., 1., 1., 1., 1., 1., 1., 1.],
   [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
   [0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0.],
   [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
   [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
   [0., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
   [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
   [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
   [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
   [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]])
A.T.indices:
array([ 0,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,  0,  1,  2,  3,
    5,  6,  7,  8,  9, 10, 11,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9,
   10, 11,  4,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,  0,  1,
    2,  3,  4,  5,  6,  7,  8,  9, 10, 11,  1,  2,  3,  4,  5,  6,  7,
    8,  9, 10, 11,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,  0,
    1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,  0,  1,  2,  3,  4,  5,
    6,  7,  8,  9, 10, 11,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10,
   11], dtype=int32)

我无法理解的是B.indices,如下所示:

B.toarray()
array([[2., 1., 1., 1., 0., 1., 1., 0., 1., 1., 1., 1.],
   [1., 2., 2., 2., 1., 2., 2., 2., 2., 2., 2., 2.],
   [1., 2., 2., 2., 0., 2., 2., 2., 2., 2., 2., 2.],
   [1., 2., 2., 2., 1., 2., 2., 2., 2., 2., 2., 2.],
   [0., 1., 0., 1., 2., 1., 1., 1., 1., 1., 1., 1.],
   [1., 2., 2., 2., 1., 2., 2., 2., 2., 2., 2., 2.],
   [1., 2., 2., 2., 1., 2., 2., 2., 2., 2., 2., 2.],
   [0., 2., 2., 2., 1., 2., 2., 2., 2., 2., 2., 2.],
   [1., 2., 2., 2., 1., 2., 2., 2., 2., 2., 2., 2.],
   [1., 2., 2., 2., 1., 2., 2., 2., 2., 2., 2., 2.],
   [1., 2., 2., 2., 1., 2., 2., 2., 2., 2., 2., 2.],
   [1., 2., 2., 2., 1., 2., 2., 2., 2., 2., 2., 2.]])
B.indices
array([ 8,  9, 11,  6,  3, 10,  5,  1,  2,  0,  
    4,  0,  7,  3, 11,  6,  8,  5, 10,  9,  2,  1,  
    0,  3,  8,  7, 11,  6,  9,  5,  1, 10,  2,  
    4,  0,  1,  2, 10,  9, 11,  5,  7,  6,  8,  3,  
    5, 10,  6,  3, 11,  8,  7,  1,  9,  4,
    4,  0,  1,  7,  2,  3,  9,  8, 10, 11,  6,  5,  
    4,  0,  1,  2,  7,  3, 10,  9, 11,  8,  5,  6,  
    4,  1,  2, 10, 11,  5,  8,  6,  3,  9,  7,  
    4,  0,  2,  1,  7, 10,  3,  5,  9, 11,  6,  8,
    4,  0,  1,  2,  3,  7,  5,  6,  8, 10, 11,  9,  
    4,  0,  7,  1,  3,  2,  8,  6,  9,  5, 11, 10,  
    4,  0,  1,  2,  7,  3,  9,  8,  6,  5, 10, 11], dtype=int32)

结果B是通过C ++计算的,因此scipy中的_sparsetools.cpython-35m-x86_64-linux-gnu.so库因此无法获取其代码。

这个问题几乎使我发疯。希望你们中的一些可以帮助我。

1 个答案:

答案 0 :(得分:0)

如果我从您的A屏幕上显示A.todense()

In [156]: A = np.array([[1., 1., 1., 1., 0., 1., 1., 0., 1., 1., 1., 1.],
     ...:    [0., 1., 1., 1., 0., 1., 1., 1., 1., 1., 1., 1.],
     ...:    [0., 1., 1., 1., 0., 1., 1., 1., 1., 1., 1., 1.],
     ...:    [0., 1., 1., 1., 0., 1., 1., 1., 1., 1., 1., 1.],
     ...:    [0., 1., 0., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
     ...:    [0., 1., 1., 1., 0., 1., 1., 1., 1., 1., 1., 1.],
     ...:    [0., 1., 1., 1., 0., 1., 1., 1., 1., 1., 1., 1.],
     ...:    [0., 1., 1., 1., 0., 1., 1., 1., 1., 1., 1., 1.],
     ...:    [0., 1., 1., 1., 0., 1., 1., 1., 1., 1., 1., 1.],
     ...:    [0., 1., 1., 1., 0., 1., 1., 1., 1., 1., 1., 1.],
     ...:    [0., 1., 1., 1., 0., 1., 1., 1., 1., 1., 1., 1.],
     ...:    [0., 1., 1., 1., 0., 1., 1., 1., 1., 1., 1., 1.]])

和其中的一个稀疏:

In [159]: M = sparse.csr_matrix(A)
In [160]: M
Out[160]: 
<12x12 sparse matrix of type '<class 'numpy.float64'>'
    with 120 stored elements in Compressed Sparse Row format>

In [162]: M.indices
Out[162]: 
array([ 0,  1,  2,  3,  5,  6,  8,  9, 10, 11,  1,  2,  3,  5,  6,  7,  8,
        9, 10, 11,  1,  2,  3,  5,  6,  7,  8,  9, 10, 11,  1,  2,  3,  5,
        6,  7,  8,  9, 10, 11,  1,  3,  4,  5,  6,  7,  8,  9, 10, 11,  1,
        2,  3,  5,  6,  7,  8,  9, 10, 11,  1,  2,  3,  5,  6,  7,  8,  9,
       10, 11,  1,  2,  3,  5,  6,  7,  8,  9, 10, 11,  1,  2,  3,  5,  6,
        7,  8,  9, 10, 11,  1,  2,  3,  5,  6,  7,  8,  9, 10, 11,  1,  2,
        3,  5,  6,  7,  8,  9, 10, 11,  1,  2,  3,  5,  6,  7,  8,  9, 10,
       11], dtype=int32)

这些索引与您的索引不同-或相同,但按行排序:

In [164]: for i in range(12):
     ...:     print(M.indices[M.indptr[i]:M.indptr[i+1]])
     ...:     
[ 0  1  2  3  5  6  8  9 10 11]
[ 1  2  3  5  6  7  8  9 10 11]
[ 1  2  3  5  6  7  8  9 10 11]
[ 1  2  3  5  6  7  8  9 10 11]
[ 1  3  4  5  6  7  8  9 10 11]
[ 1  2  3  5  6  7  8  9 10 11]
[ 1  2  3  5  6  7  8  9 10 11]
[ 1  2  3  5  6  7  8  9 10 11]
[ 1  2  3  5  6  7  8  9 10 11]
[ 1  2  3  5  6  7  8  9 10 11]
[ 1  2  3  5  6  7  8  9 10 11]
[ 1  2  3  5  6  7  8  9 10 11]

对于其转置的csr版本:

In [165]: M1 = M.T.tocsr()
In [166]: for i in range(12):
     ...:     print(M1.indices[M1.indptr[i]:M1.indptr[i+1]])

[0]
[ 0  1  2  3  4  5  6  7  8  9 10 11]
[ 0  1  2  3  5  6  7  8  9 10 11]
[ 0  1  2  3  4  5  6  7  8  9 10 11]
[4]
[ 0  1  2  3  4  5  6  7  8  9 10 11]
[ 0  1  2  3  4  5  6  7  8  9 10 11]
[ 1  2  3  4  5  6  7  8  9 10 11]
[ 0  1  2  3  4  5  6  7  8  9 10 11]
[ 0  1  2  3  4  5  6  7  8  9 10 11]
[ 0  1  2  3  4  5  6  7  8  9 10 11]
[ 0  1  2  3  4  5  6  7  8  9 10 11]

通过切换为具有相同属性的csc格式来进行移调很快,但是正如您注意到的那样,它已经转换回csr

然后取总和:

In [167]: B = M+M.T
In [168]: for i in range(12):
     ...:     print(B.indices[B.indptr[i]:B.indptr[i+1]])

[ 0  1  2  3  5  6  8  9 10 11]
[ 0  1  2  3  4  5  6  7  8  9 10 11]
[ 0  1  2  3  5  6  7  8  9 10 11]
[ 0  1  2  3  4  5  6  7  8  9 10 11]
[ 1  3  4  5  6  7  8  9 10 11]
[ 0  1  2  3  4  5  6  7  8  9 10 11]
[ 0  1  2  3  4  5  6  7  8  9 10 11]
[ 1  2  3  4  5  6  7  8  9 10 11]
[ 0  1  2  3  4  5  6  7  8  9 10 11]
[ 0  1  2  3  4  5  6  7  8  9 10 11]
[ 0  1  2  3  4  5  6  7  8  9 10 11]
[ 0  1  2  3  4  5  6  7  8  9 10 11]

由于M索引是按行排序的,因此模式非常明显。

In [172]: M.has_sorted_indices
Out[172]: True

我的版本也是canonical

A的所有行都有10个非零项。 B有10、11、12;转置为0-2非零填充。

您的A具有未排序的索引。 A.T.indices已排序。我想可能会通过将无序B.indicesA.indices进行比较来推断出评估方法。但是这值得吗?

===

我使用以下方法重新创建了您的非规范矩阵:

In [186]: altindices = np.array([ 0,  2,  1,  5, 10,  3,  6, 11,  9,  8,  
     ...:     1,  2,  9, 10,  5,  8,  6, 11,  3,  7,  
     ...:     2, 10,  1,  5,  9,  6, 11,  7,  8,  3,  
     ...:     3,  8,  6,  7,  5, 11,  9, 10,  2,  1,  
     ...:     4,  9,  1,  7,  8, 11,  3,  6, 10,  5,  
     ...:     5,  6, 11, 10,  8,  9,  3,  2,  7,  1,  
     ...:     6,  5,  8, 11,  9, 10,  3,  7,  2,  1,  
     ...:     7,  9,  3,  6,  8,  5, 11, 10,  2,  1,  
     ...:     8,  6, 11,  9,  5,  3, 10,  7,  1,  2,  
     ...:     9, 11, 10,  8,  6,  5,  7,  3,  2,  1, 
     ...:     10, 11, 5,  9,  6,  8,  2,  3,  1,  7, 
     ...:     11, 10,  5,  6,  8,  9,  3,  7,  2, 1], dtype='int32')
    In [188]: M2 = sparse.csr_matrix((M.data, altindices, M.indptr))

M2.T.indicesM2.indices(等效于csc)相同,但是M2.T.tocsr()在显示时进行了排序。 B2 = M2+M2.T的索引与您一样。

如果仅评估一行,我将得到相同的索引,确认我的猜测是它逐行执行求和(如上所述,使用indptr进行迭代):

In [194]: M2[0,:]+(M2.T)[0,:]
Out[194]: 
<1x12 sparse matrix of type '<class 'numpy.float64'>'
    with 10 stored elements in Compressed Sparse Row format>
In [195]: _.indices
Out[195]: array([ 8,  9, 11,  6,  3, 10,  5,  1,  2,  0], dtype=int32)