Scipy 稀疏矩阵的除法

5
我一直在尝试将Python Scipy稀疏矩阵除以其行向量之和。以下是我的代码:

sparse_mat = bsr_matrix((l_data, (l_row, l_col)), dtype=float)
sparse_mat = sparse_mat / (sparse_mat.sum(axis = 1)[:,None])

然而,无论我如何尝试,它都会报错。
sparse_mat = sparse_mat / (sparse_mat.sum(axis = 1)[:,None])
File "/usr/lib/python2.7/dist-packages/scipy/sparse/base.py", line 381, in __div__
return self.__truediv__(other)
File "/usr/lib/python2.7/dist-packages/scipy/sparse/compressed.py", line 427, in __truediv__
raise NotImplementedError
NotImplementedError

我哪里做错了吗?


该除法调用了true_division,它是一种逐元素的除法。这似乎没有为多个值实现。因此,很可能(sparse_mat.sum(axis=1)[:, None])的结果不是单个数字。 - Dschoni
@Dschoni 是的,结果是一个向量,我的目标是将稀疏矩阵中每一行的每个元素除以该行元素之和。所以如果 M=[[2,4],[1,2]],我想得到 Ans=[[2/6, 4/6],[1/3, 2/3]]。 - uchman21
你尝试过 sparse_mat = sparse_mat*(1 / (sparse_mat.sum(axis = 1)[:,None])) 吗?看起来稀疏矩阵的除法是问题所在。你可能还需要将除数转换为密集数组 sparse_mat = sparse_mat*(1 / (sparse_mat.sum(axis = 1).toarray()[:,None])) - Daniel F
@uchman21 请提供一个小的自包含示例。这个问题可能与您放入矩阵的数据有关。(或者可能是因为您的scipy太旧了——就像我尝试过的那样,在Python 3和scipy 0.18上工作的稀疏矩阵除法一样。) - MB-F
我正在使用Python 2.7.13和Scipy 0.18。该矩阵只是一个232 x 232的简单稀疏矩阵。 - uchman21
我想知道这是否是Py2的问题。在Py3中,我可以毫无问题地进行除法运算。 - hpaulj
3个回答

9
你可以通过将行和的倒数创建为稀疏对角矩阵,然后将其与你的矩阵相乘来避免这个问题。在乘积中,对角矩阵位于左侧,而你的矩阵位于右侧。
示例:
>>> a
array([[0, 9, 0, 0, 1, 0],
       [2, 0, 5, 0, 0, 9],
       [0, 2, 0, 0, 0, 0],
       [2, 0, 0, 0, 0, 0],
       [0, 9, 5, 3, 0, 7],
       [1, 0, 0, 8, 9, 0]])
>>> b = sparse.bsr_matrix(a)
>>> 
>>> c = sparse.diags(1/b.sum(axis=1).A.ravel())
>>> # on older scipy versions the offsets parameter (default 0)
... # is a required argument, thus
... # c = sparse.diags(1/b.sum(axis=1).A.ravel(), 0)
...
>>> a/a.sum(axis=1, keepdims=True)
array([[ 0.        ,  0.9       ,  0.        ,  0.        ,  0.1       ,  0.        ],
       [ 0.125     ,  0.        ,  0.3125    ,  0.        ,  0.        ,  0.5625    ],
       [ 0.        ,  1.        ,  0.        ,  0.        ,  0.        ,  0.        ],
       [ 1.        ,  0.        ,  0.        ,  0.        ,  0.        ,  0.        ],
       [ 0.        ,  0.375     ,  0.20833333,  0.125     ,  0.        ,  0.29166667],
       [ 0.05555556,  0.        ,  0.        ,  0.44444444,  0.5       ,  0.        ]])
>>> (c @ b).todense() # on Python < 3.5 replace c @ b with c.dot(b)
matrix([[ 0.        ,  0.9       ,  0.        ,  0.        ,  0.1       ,  0.        ],
        [ 0.125     ,  0.        ,  0.3125    ,  0.        ,  0.        ,  0.5625    ],
        [ 0.        ,  1.        ,  0.        ,  0.        ,  0.        ,  0.        ],
        [ 1.        ,  0.        ,  0.        ,  0.        ,  0.        ,  0.        ],
        [ 0.        ,  0.375     ,  0.20833333,  0.125     ,  0.        ,  0.29166667],
        [ 0.05555556,  0.        ,  0.        ,  0.44444444,  0.5       ,  0.        ]])

我尝试了这个解决方案,但它报错了。elem_sum = csc_matrix((1/sparse_mat.sum(axis = -1).A.ravel(), numpy.arange(sparse_mat.shape[0]), numpy.arange(sparse_mat.shape[0]+1))) File "/usr/lib/python2.7/dist-packages/scipy/sparse/compressed.py", line 548, in sum return spmatrix.sum(self,axis) File "/usr/lib/python2.7/dist-packages/scipy/sparse/base.py", line 629, in sum raise ValueError("axis out of bounds") ValueError: axis out of bounds - uchman21
@uchman21 奇怪,尝试使用 axis = 1,在您的代码中似乎可以解决问题。 - Paul Panzer
是的,那个方案可行。但是对于我而言,我需要像这样制定一个主对角线才能使它最终工作:c = sparse.diags(1/b.sum(axis=1).A.ravel(),0)。请将其添加到您的答案中。 - uchman21
2
b.sum(axis=1).A1 应该可以正常工作。sum 会产生一个 np.matrix,其中有一个 A1 属性。https://dev59.com/oHA75IYBdhLWcg3wW3u8#20765358 - hpaulj

3
根据这个消息,为了保持矩阵的稀疏性,您可以访问数据值并使用(非零)索引:
sums = np.asarray(A.sum(axis=1)).squeeze()  # this is dense
A.data /= sums[A.nonzero()[0]]

如果使用非零行均值进行除法而不是总和,则可以

nnz = A.getnnz(axis=1)  # this is also dense
means = sums / nnz
A.data /= means[A.nonzero()[0]]

3

有些有趣的事情正在发生。我没有问题执行元素分割。我想知道这是否是Py2的问题。我正在使用Py3。

In [1022]: A=sparse.bsr_matrix([[2,4],[1,2]])
In [1023]: A
Out[1023]: 
<2x2 sparse matrix of type '<class 'numpy.int32'>'
    with 4 stored elements (blocksize = 2x2) in Block Sparse Row format>
In [1024]: A.A
Out[1024]: 
array([[2, 4],
       [1, 2]], dtype=int32)
In [1025]: A.sum(axis=1)
Out[1025]: 
matrix([[6],
        [3]], dtype=int32)
In [1026]: A/A.sum(axis=1)
Out[1026]: 
matrix([[ 0.33333333,  0.66666667],
        [ 0.33333333,  0.66666667]])

或者尝试另一个示例:
In [1027]: b=sparse.bsr_matrix([[0, 9, 0, 0, 1, 0],
      ...:        [2, 0, 5, 0, 0, 9],
      ...:        [0, 2, 0, 0, 0, 0],
      ...:        [2, 0, 0, 0, 0, 0],
      ...:        [0, 9, 5, 3, 0, 7],
      ...:        [1, 0, 0, 8, 9, 0]])
In [1028]: b
Out[1028]: 
<6x6 sparse matrix of type '<class 'numpy.int32'>'
    with 14 stored elements (blocksize = 1x1) in Block Sparse Row format>
In [1029]: b.sum(axis=1)
Out[1029]: 
matrix([[10],
        [16],
        [ 2],
        [ 2],
        [24],
        [18]], dtype=int32)
In [1030]: b/b.sum(axis=1)
Out[1030]: 
matrix([[ 0.        ,  0.9       ,  0.        ,  0.        ,  0.1       , 0.        ],
        [ 0.125     ,  0.        ,  0.3125    ,  0.        ,  0.        , 0.5625    ],
 ....
        [ 0.05555556,  0.        ,  0.        ,  0.44444444,  0.5       ,     0.        ]])

这个稀疏/密集的结果也是密集的,其中 c*bc 是稀疏对角线)是稀疏的。
In [1039]: c*b
Out[1039]: 
<6x6 sparse matrix of type '<class 'numpy.float64'>'
    with 14 stored elements in Compressed Sparse Row format>

稀疏求和是一个密集矩阵。它是二维的,所以不需要扩展其维度。事实上,如果我尝试这样做,就会出现错误:
In [1031]: A/(A.sum(axis=1)[:,None])
....
ValueError: shape too large to be a matrix.

似乎这取决于scipy的版本。使用过时的版本,我实际上按预期工作,其中两个稀疏向量的除法返回了一个稀疏向量。毕竟,如果被除数有一个空单元格,在该单元格中结果应为0。对于较新版本的scipy,相同的行返回一个密集的numpy矩阵... - Radio Controlled

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接