添加切片

时间:2015-09-02 16:09:37

标签: python scipy sparse-matrix

我正在尝试将scipy稀疏csc矩阵的片段加在一起。我现在这样做的方式,出现了具有正确值的矩阵,但出于神秘的原因,存储元素的数量增加了。它以数学方式增长到矩阵中非零元素的数量。怎么样?

示例:

import numpy as np
from scipy.sparse import *

#define two dense matrices
M1_d = np.array([[0, 0, 1, 2, 3],
                 [4, 5, 0, 0, 0],
                 [6, 0, 0, 7, 8],
                 [9,10, 0,11,12],
                 [0, 0, 0, 0, 0]])
M2_d = 2*M1_d

#define their sparse counterparts (in CSC form)
M1_s = csc_matrix(M1_d)
M2_s = csc_matrix(M2_d)

#doing addition with both forms works fine
Mp_d = M1_d + M2_d
Mp_s = M1_s + M2_s

在这个正常加法中,Mp_d和Mp_s具有相同的结果,并且正确的元素数存储在稀疏矩阵中Mp_s

>>>Mp_s
<5x5 sparse matrix of type '<type 'numpy.int32'>'
with 12 stored elements in Compressed Sparse Column format>

另一方面,当我尝试仅将矩阵的切片添加到一起并将其存储在相同大小的矩阵中时。出现了正确的值,但是一些新元素存储在稀疏矩阵中

#initialise some matrices as copies
Mslicep_d = M1_d.copy()
Mslicep_s = M1_s.copy()

#now I want to add only slices of the matrices
clm = [0,2,3] # I want to add these colums
Mslicep_d[:,clm] = M1_d[:,clm] + M2_d[:,clm]
Mslicep_s[:,clm] = M1_s[:,clm] + M2_s[:,clm]

矩阵Mslicep_s应该再次有12个存储元素。看起来确实如此,但是:

>>>Mslicep_s
<5x5 sparse matrix of type '<type 'numpy.int32'>'
with 20 stored elements in Compressed Sparse Column format>
>>>Mslicep_s.toarray()
array([[ 0,  0,  3,  6,  3],
       [12,  5,  0,  0,  0],
       [18,  0,  0, 21,  8],
       [27, 10,  0, 33, 12],
       [ 0,  0,  0,  0,  0]])

如果你在这里计算数值,你会看到其中有12个。但由于某种原因,稀疏矩阵有20个存储元素。

当我在循环中执行多个这些添加时,矩阵变得越来越稀疏,这使得我的代码需要很长时间。谁能告诉我这些新存储元素来自哪里?怎么预防这个?或者至少如何在每次迭代时快速修复它?

提前致谢。

编辑:让我说清楚。我想执行矩阵的稀疏结构不会改变的附加物。

1 个答案:

答案 0 :(得分:0)

我第一次添加问题时会收到警告:

In [344]: Mslicep_s[:,clm] = M1_s[:,clm] + M2_s[:,clm]
/usr/local/lib/python2.7/site-packages/scipy/sparse/compressed.py:728: SparseEfficiencyWarning: Changing the sparsity structure of a csc_matrix is expensive. lil_matrix is more efficient.
  SparseEfficiencyWarning)

这些额外的8个元素都是0,如Mslicep_s.data中所示。显然它是插入密集版本的总和:

In [355]: (M1_s[:,clm] + M2_s[:,clm]).A
Out[355]: 
array([[ 0,  3,  6],
       [12,  0,  0],
       [18,  0, 21],
       [27,  0, 33],
       [ 0,  0,  0]])

我的猜测是有利于速度而不是存储。

我清理矩阵:

In [347]: Mslicep_s.eliminate_zeros()    
In [348]: Mslicep_s
Out[348]: 
<5x5 sparse matrix of type '<type 'numpy.int32'>'
    with 12 stored elements in Compressed Sparse Column format>

因此,作为立即改进,您可以在每次迭代时执行此步骤。

对于密集阵列,选择带有clm之类列表的列是高级索引&#39;,并生成副本,而不是更快的视图。 csc格式是选择此类列的最佳稀疏格式,但M1_s[:,clm]仍然是副本。正如您所看到的,将值插入csc矩阵既耗费时间又占用空间。我不确定更改Mslicep_s的格式是否有帮助。 lil应该更好地修改稀疏性,尽管改变行会比列更好。

值得记住的是,csccsr格式针对矩阵乘法计算进行了优化。他们甚至用.sum()做乘法。一般来说,如果你可以用这些术语来计算你的计算,你会得到更好的表现,包括时间和记忆。索引不是稀疏matricies的强项。

如果您进行了计算,请不要更改稀疏度(不要添加非零值),然后您可以直接对.data属性数组进行更改。但它确实需要了解存储布局。

<todo: code to translate [:,clm] to idx array>
Mslice_s.data[idx] = M1_s.data[idx] + M2_s.data[idx]

这似乎是正确的idx

idx = np.concatenate([np.arange(M1_s.indptr[i], M1_s.indptr[i+1]) for i in clm])
# array([0, 1, 2, 5, 6, 7, 8])

可能有更直接的计算方法,但我认为它有正确的想法 - 根据所选的.indptr值选择范围。