从SciPy稀疏矩阵获取左,右,上,下非零邻居

时间:2019-01-08 13:53:39

标签: python numpy matrix scipy sparse-matrix

说我有一个2D SciPy稀疏矩阵:

import numpy as np
from scipy.sparse import csc_matrix

arr = np.array([[0, 0, 1, 0, 1],
                [1, 0, 0, 1, 0],
                [0, 1, 1, 0, 0],
                [1, 0, 0, 1, 0],
                [0, 1, 0, 0, 0],
               ])

csc = csc_matrix(arr)

对于矩阵中的每个非零元素,我想创建四个新的稀疏矩阵,它们包含与下一个最近的Left,Right,Up和Down非零邻居相对应的索引。末端的元素可以具有环绕的相邻元素(考虑水平和垂直方向上的圆形双向链表或环形)。如果元素是其行/列中唯一的非零元素,则相应的索引将指向自身。另外,由于索引可以具有零值(在引用第一行或第一列时),并且与自然零元素没有区别,因此我们将这些零索引设置为-1,以使真实索引与零元素有歧义。

对于上面的矩阵,密集的Left和Down矩阵如下所示:

left = np.array([[0, 0, 4,  0, 2],
                 [3, 0, 0, -1, 0],
                 [0, 2, 1,  0, 0],
                 [3, 0, 0, -1, 0],
                 [0, 1, 0,  0, 0],
                ])

down = np.array([[0, 0,  2, 0, -1],
                 [3, 0,  0, 3,  0],
                 [0, 4, -1, 0,  0],
                 [1, 0,  0, 1,  0],
                 [0, 2,  0, 0,  0],
                ])

请记住,索引值为-1的元素实际上是对索引零的引用。当然,我需要以稀疏矩阵形式存储这些矩阵,因为我的真实矩阵太大且稀疏,无法容纳到内存中。

4 个答案:

答案 0 :(得分:1)

这是做左邻居的一种可能方法。 它不是特别有效,但如果整个矩阵中没有很多非零条目,可能就可以了。您可以通过获取行中每行的非零条目并仅计算一次"bad json"来对其进行稍微优化。

请注意,我只是将索引上移了一个,而不是将j[i==row]设置为0

-1

答案 1 :(得分:1)

In [183]: arr = np.array([[0, 0, 1, 0, 1],
     ...:                 [1, 0, 0, 1, 0],
     ...:                 [0, 1, 1, 0, 0],
     ...:                 [1, 0, 0, 1, 0],
     ...:                 [0, 1, 0, 0, 0],
     ...:                ])
     ...:                
In [184]: from scipy import sparse
In [185]: M = sparse.lil_matrix(arr)
In [186]: M.rows
Out[186]: 
array([list([2, 4]), list([0, 3]), list([1, 2]), list([0, 3]), list([1])],
      dtype=object)

与使用以下方法从密集数组中获得的信息相同:

In [187]: [np.where(row)[0] for row in arr]
Out[187]: [array([2, 4]), array([0, 3]), array([1, 2]), array([0, 3]), array([1])]

我假设您已经想出了如何从密集数组中生成所需的left(或right),所以我不会深入探讨这些细节(我太懒了,无法进行搏斗以及您的包装规格)。

对于列:

 In [189]: M.T.rows
 Out[189]: 
 array([list([1, 3]), list([2, 4]), list([0, 2]), list([1, 3]), list([0])],
  dtype=object)

可以使用csc格式:

In [190]: Mc = sparse.csc_matrix(arr)
In [191]: Mc.indptr
Out[191]: array([0, 2, 4, 6, 8, 9], dtype=int32)
In [192]: Mc.indices
Out[192]: array([1, 3, 2, 4, 0, 2, 1, 3, 0], dtype=int32)
In [193]: for i in range(5):
     ...:     print(Mc.indices[Mc.indptr[i]:Mc.indptr[i+1]])
     ...:     
[1 3]
[2 4]
[0 2]
[1 3]
[0]

在此示例中,所有行或列都只有1个或2个非零。我想在更大的范围内,会有很多非零。同样,对于csc(和csr,还有可能未对每个“行”的索引进行排序-有一种稀疏的方法可以解决该问题。

关于构建返回稀疏矩阵,您可以修改副本的data属性(它将具有相同的稀疏性)。

In [194]: M.data
Out[194]: 
array([list([1, 1]), list([1, 1]), list([1, 1]), list([1, 1]), list([1])],
      dtype=object)
In [195]: Mc.data
Out[195]: array([1, 1, 1, 1, 1, 1, 1, 1, 1], dtype=int64)

或从数组构造稀疏矩阵(这对于coo样式输入是正常的)。


在我的lil版本中,tch's解决方案的速度要适中:

ind = sparse.lil_matrix(M.shape,dtype='int')
for i,row in enumerate(M.rows):
    k = np.array(row)
    ind[i,k] = np.roll(k+1,1)

以我替换data的想法更好:

ind = M.copy()
for row,dat in zip(ind.rows,ind.data):
    k = np.array(row)
    dat[:] = np.roll(k+1,1).tolist()

或与Mr = Mc.tocsr()

ind = Mr.copy()
for i in range(Mr.shape[0]):
    slc = slice(Mr.indptr[i],Mr.indptr[i+1])
    k = Mr.indices[slc]
    ind.data[slc] = np.roll(k+1,1)

答案 2 :(得分:0)

一个可能的答案(密集形式):

ix, iy = csc.nonzero()
w = np.where(np.insert(np.diff(ix), 0,1) != 0)[0]
iy2 = np.concatenate([np.roll(_, 1) for _ in np.split(iy,w)])
iy2[iy2==0] = -1

left = csc_matrix(arr.shape)
left[ix, iy] = iy2

ix, iy = csc.transpose().nonzero()
w = np.where(np.insert(np.diff(ix), 0,1) != 0)[0]
iy2 = np.concatenate([np.roll(_, 1) for _ in np.split(iy,w)])
iy2[iy2==0] = -1

down = csc_matrix(arr.T.shape)
down[ix, iy] = iy2
down = down.transpose()
print(left.todense(), '\n', down.todense())


 >> [[ 0  0  4  0  2]
 [ 3  0  0 -1  0]
 [ 0  2  1  0  0]
 [ 3  0  0 -1  0]
 [ 0  1  0  0  0]]

[[ 0  0  2  0 -1]
 [ 3  0  0  3  0]
 [ 0  4 -1  0  0]
 [ 1  0  0  1  0]
 [ 0  2  0  0  0]]

答案 3 :(得分:0)

一种更加矢量化的方法:

csc = csc_matrix(arr)
inds = (csc.indices,csc.indptr)
irows = np.split(*inds)[1:-1]

down = csc_matrix((np.hstack([np.roll(row,-1) for row in irows]),*inds))
up = csc_matrix((np.hstack([np.roll(row,1) for row in irows]),*inds))

检查:

>>> down.A 
array([[0, 0, 2, 0, 0],
       [3, 0, 0, 3, 0],
       [0, 4, 0, 0, 0],
       [1, 0, 0, 1, 0],
       [0, 2, 0, 0, 0]], dtype=int32)

左和右可以通过CSR表示获得。

我认为用-1编码0是个好主意,因为如果这样做会破坏所有稀疏计算的改进。只有csc.nonzeros()设计的地方必须参观。