在scipy稀疏矩阵上将掩码为True的元素设置为零的有效方法

hir*_*228 9 python mask matrix scipy sparse-matrix

我有两个scipy_sparse_csr_matrix'a'和scipy_sparse_csr_matrix(boolean)'mask',我想将'a'的元素设置为零,其中mask的元素为True.

例如

>>>a
<3x3 sparse matrix of type '<type 'numpy.int32'>'
    with 4 stored elements in Compressed Sparse Row format>
>>>a.todense()
matrix([[0, 0, 3],
        [0, 1, 5],
        [7, 0, 0]])

>>>mask
<3x3 sparse matrix of type '<type 'numpy.bool_'>'
    with 4 stored elements in Compressed Sparse Row format>
>>>mask.todense()
matrix([[ True, False,  True],
        [False, False,  True],
        [False,  True, False]], dtype=bool)
Run Code Online (Sandbox Code Playgroud)

然后我想获得以下结果.

>>>result
<3x3 sparse matrix of type '<type 'numpy.int32'>'
    with 2 stored elements in Compressed Sparse Row format>
>>>result.todense()
matrix([[0, 0, 0],
        [0, 1, 0],
        [7, 0, 0]])
Run Code Online (Sandbox Code Playgroud)

我可以通过操作来做到这一点

result = a - a.multiply(mask)
Run Code Online (Sandbox Code Playgroud)

要么

a -= a.multiply(mask) #I don't care either in-place or copy.
Run Code Online (Sandbox Code Playgroud)

但我认为上述操作效率低下.由于'a'和'mask'的实际形状为67,108,864×2,000,000,因此这些操作在高规格服务器(64核Xeon cpu,512GB内存)上需要几秒钟.例如,'a'有大约30,000,000个非零元素,'mask'有大约1,800,000个非零(True)元素,然后上面的操作大约需要2秒.

有更有效的方法吗?

条件如下.

  1. a.getnnz()!= mask.getnnz()
  2. a.shape = mask.shape

谢谢!

其他方式(试过)

a.data*=~np.array(mask[a.astype(np.bool)]).flatten();a.eliminate_zeros() #This takes twice the time longer than above method.
Run Code Online (Sandbox Code Playgroud)

hpa*_*ulj 1

我的第一印象是这种乘法和减法方法是合理的。代码经常sparse将运算实现为某种乘法,即使密集的等效项使用更直接的方法。行或列上的稀疏求和使用与适当的 1 行或列矩阵的矩阵乘法。甚至行或列索引也使用矩阵乘法(至少在csr格式上)。

有时我们可以通过直接使用矩阵属性(data, indices, indptr)来改进操作。但这需要更多的思考和实验。

对于密集阵列,我的第一次尝试是

In [611]: a.A*~(mask.A)
Out[611]: 
array([[0, 0, 0],
       [0, 1, 0],
       [7, 0, 0]], dtype=int32)
Run Code Online (Sandbox Code Playgroud)

但没有直接的方法来处理not稀疏矩阵。如果mask确实稀疏,~mask就不会了。在您的示例中,mask有 4 个 True 项和 5 个 False,因此密集版本也可以正常工作:

In [612]: nmask=sparse.csr_matrix(~(mask.A))
In [615]: a.multiply(nmask)
Out[615]: 
<3x3 sparse matrix of type '<class 'numpy.int32'>'
    with 2 stored elements in Compressed Sparse Row format>
Run Code Online (Sandbox Code Playgroud)

CSR scipy 矩阵在更新其值后不更新探索将稀疏矩阵的对角线设置为 0。可以将属性的值设置data为 0,然后eliminate_zeros在最后一次。

另一种密集方法是

In [618]: a1=a.A
In [619]: a1[mask.A]=0
Run Code Online (Sandbox Code Playgroud)

这也适用于sparse- 有点

In [622]: a2=a.copy()
In [624]: a2[mask]
Out[624]: matrix([[0, 3, 5, 0]], dtype=int32)
In [625]: a2[mask]=0
/usr/local/lib/python3.5/dist-packages/scipy/sparse/compressed.py:730: SparseEfficiencyWarning: Changing the sparsity structure of a csr_matrix is expensive. lil_matrix is more efficient.
  SparseEfficiencyWarning)
In [626]: a2
Out[626]: 
<3x3 sparse matrix of type '<class 'numpy.int32'>'
    with 6 stored elements in Compressed Sparse Row format>
Run Code Online (Sandbox Code Playgroud)

正如上一个问题所述,我们需要消除零:

In [628]: a2.eliminate_zeros()
In [629]: a2
Out[629]: 
<3x3 sparse matrix of type '<class 'numpy.int32'>'
    with 2 stored elements in Compressed Sparse Row format>
Run Code Online (Sandbox Code Playgroud)

从稀疏警告中得到提示,让我们尝试一下lil格式

In [638]: al=a.tolil()
In [639]: al[mask]
Out[639]: 
<1x4 sparse matrix of type '<class 'numpy.int32'>'
    with 2 stored elements in LInked List format>
In [640]: al[mask]=0
In [641]: al
Out[641]: 
<3x3 sparse matrix of type '<class 'numpy.int32'>'
    with 2 stored elements in LInked List format>
Run Code Online (Sandbox Code Playgroud)

有趣的是,al[mask]仍然是稀疏的,而 asa[mask]是密集的。这两种格式使用不同的索引方法。

在稀疏度较低的情况下,可能值得迭代 的 True(非零)元素mask,直接将 的相应项设置a为零。

我不会猜测这些方法的相对速度。这需要用实际数据进行测试。