在scipy中更新稀疏连接矩阵

时间:2017-10-06 08:27:01

标签: python scipy sparse-matrix

我正在使用连接矩阵,它是图数据结构的表示。 NxM矩阵对应于具有M个顶点的N个边缘(它可能具有比顶点更多的边缘,这就是我使用scipy' s csr_matrix的原因)。 "开始"边缘的点由" -1和#34;表示。并且终点由" 1"表示。在连接矩阵中。所有其他值均为0,因此每行只有2个非零值。

我需要整合一个"细分"方法,将有效地更新连接矩阵。目前我正在将连接矩阵转换为密集矩阵,因此我可以添加新的行/列并更新旧的行/列。我正在转换为密集矩阵,因为我找不到找到用于更新旧边连接的列索引的解决方案(没有等效的scipy.where),并且csr表示不允许我通过索引更新值。 / p>

from numpy import where, array, zeros, hstack, vstack
from scipy.sparse import coo_matrix, csr_matrix

def connectivity_matrix(edges):
    m    = len(edges)
    data = array([-1] * m + [1] * m)
    rows = array(list(range(m)) + list(range(m)))
    cols = array([edge[0] for edge in edges] + [edge[1] for edge in edges])
    C    = coo_matrix((data, (rows, cols))).asfptype()
    return C.tocsr()

def subdivide_edges(C, edge_indices):
    C = C.todense()
    num_e = C.shape[0] # number of edges
    num_v = C.shape[1] # number of vertices

    for edge in edge_indices:
        num_e += 1 # increment row (edge count)
        num_v += 1 # increment column (vertex count)
        _, start = where(C[edge] == -1.0)
        _, end   = where(C[edge] == 1.0)
        si    = start[0]
        ei    = end[0]
        # add row
        r, c = C.shape
        new_r = zeros((1, c))
        C = vstack([C, new_r])
        # add column
        r, c = C.shape
        new_c = zeros((r, 1))
        C = hstack([C, new_c])
        # edit edge start/end points
        C[edge, ei] = 0.0
        C[edge, num_v - 1] = 1.0
        # add new edge start/end points
        C[num_e - 1, ei] = 1.0
        C[num_e - 1, num_v - 1] = -1.0

    return csr_matrix(C)

edges = [(0, 1), (1, 2)] # edge connectivity
C = connectivity_matrix(edges)
C = subdivide_edges(C, [0, 1])
# new edge connectivity: [(0, 3), (1, 4), (3, 1), (4, 2)]

1 个答案:

答案 0 :(得分:0)

稀疏矩阵确实有nonzero方法(np.where使用np.nonzero)。但是看看它的代码 - 它会返回coo行/列数据。

使用另一个问题留下的稀疏矩阵:

In [468]: M
Out[468]: 
<5x5 sparse matrix of type '<class 'numpy.float64'>'
    with 5 stored elements in COOrdinate format>
In [469]: Mc = M.tocsr()
In [470]: Mc.nonzero()
Out[470]: (array([0, 1, 2, 3, 4], dtype=int32), array([2, 0, 4, 3, 1], dtype=int32))
In [471]: Mc[1,:].nonzero()
Out[471]: (array([0]), array([0]))
In [472]: Mc[3,:].nonzero()
Out[472]: (array([0]), array([3]))

我转换为csr来做行索引。

还有一个稀疏的vstack

但与密集阵列相比,稀疏矩阵的迭代工作很慢。

警惕像C[edge] == -1.0这样的浮动比较。 ==测试可以更好地使用整数。

将值从零更改为非零会引发警告,但确实有效:

In [473]: Mc[1,1] = 23
/usr/local/lib/python3.5/dist-packages/scipy/sparse/compressed.py:774: SparseEfficiencyWarning: Changing the sparsity structure of a csr_matrix is expensive. lil_matrix is more efficient.
  SparseEfficiencyWarning)
In [474]: (Mc[1,:]==23).nonzero()
Out[474]: (array([0]), array([1]))

将非零值更改为零不会产生警告,但它也不会更改基础稀疏性(直到清除矩阵)。 lil格式更适合逐个元素更改。

In [478]: Ml = M.tolil()
In [479]: Ml.nonzero()
Out[479]: (array([0, 1, 2, 3, 4], dtype=int32), array([2, 0, 4, 3, 1], dtype=int32))
In [480]: Ml[1,:].nonzero()
Out[480]: (array([0], dtype=int32), array([0], dtype=int32))
In [481]: Ml[1,2]=.5
In [482]: Ml[1,:].nonzero()
Out[482]: (array([0, 0], dtype=int32), array([0, 2], dtype=int32))
In [483]: (Ml[1,:]==.5).nonzero()
Out[483]: (array([0], dtype=int32), array([2], dtype=int32))

In [486]: sparse.vstack((Ml,Ml),format='lil')
Out[486]: 
<10x5 sparse matrix of type '<class 'numpy.float64'>'
    with 12 stored elements in LInked List format>

sparse.vstack的工作原理是将输入转换为coo,并加入其属性(行,列,数据)和制作新矩阵。

我怀疑您的代码将使用lil矩阵,而无需进行太多更改。但它可能会慢一些。稀疏在低密度矩阵上执行矩阵乘法等时获得最佳速度。当密集的等价物太大而不适合存储器时,它也有帮助。但是对于迭代工作和不断增长的矩阵来说,它很慢。