【问题标题】:How to efficiently make new matrix from sum of blocks from bigger sparse matrix如何从更大的稀疏矩阵中的块总和有效地制作新矩阵
【发布时间】:2012-12-19 10:50:19
【问题描述】:

我有一个大的 scipy 稀疏对称矩阵,我需要通过对块求和来压缩它以生成一个新的更小的矩阵。

例如,对于 4x4 稀疏矩阵 A,我想创建一个 2x2 矩阵 B,其中 B[i,j] = sum(A[i:i+2,j:j+2])。

目前,我只是逐块重新创建压缩矩阵,但这很慢。关于如何优化它的任何想法?

更新:这是一个运行良好的示例代码,但对于我想压缩为 10.000x10.000 的 50.000x50.000 的稀疏矩阵来说速度很慢:

>>> A = (rand(4,4)<0.3)*rand(4,4)
>>> A = scipy.sparse.lil_matrix(A + A.T) # make the matrix symmetric

>>> B = scipy.sparse.lil_matrix((2,2))
>>> for i in range(B.shape[0]):
...     for j in range(B.shape[0]):
...         B[i,j] = A[i:i+2,j:j+2].sum()

【问题讨论】:

  • 你试过用numpy的A[i:i+2,j:j+2].sum()而不是python的sum吗?
  • 如何确定块大小?修复了吗?
  • @wim 其实我用的是稀疏矩阵求和。
  • @NPE 块大小不固定。是大矩阵大小的除数。

标签: python matrix scipy sparse-matrix


【解决方案1】:

首先,lil 你总结的矩阵可能真的很糟糕,我会尝试COO 或者CSR/CSS(我不知道哪个会更好,但lil 是对于这些操作中的许多操作来说,可能天生就比较慢,即使切片也可能要慢得多,尽管我没有测试)。 (除非您知道例如 dia 非常适合)

基于COO,我可以想象做一些恶作剧。因为COOrowcol 数组来给出确切的位置:

matrix = A.tocoo()

new_row = matrix.row // 5
new_col = matrix.col // 5
bin = (matrix.shape[0] // 5) * new_col + new_row
# Now do a little dance because this is sparse,
# and most of the possible bin should not be in new_row/new_col
# also need to group the bins:
unique, bin = np.unique(bin, return_inverse=True)
sum = np.bincount(bin, weights=matrix.data)
new_col = unique // (matrix.shape[0] // 5)
new_row = unique - new_col * (matrix.shape[0] // 5)

result = scipy.sparse.coo_matrix((sum, (new_row, new_col)))

(我不能保证我没有在某处混淆行和列,这只适用于方阵......)

【讨论】:

  • 哇,这是一个绝妙的解决方案!谢谢!我不知道 np.bincount
【解决方案2】:

给定一个大小为 N 的方阵和一个 d 的分割大小(因此矩阵将被划分为 N/d * N/d 个大小为 d 的子矩阵),你能不能使用 numpy.split 几次来构建这些子矩阵的集合,对每个子矩阵求和,然后将它们放入复合了?

这应该更多地被视为伪代码而不是有效的实现,但它表达了我的想法:

    def chunk(matrix, size):
        row_wise = []
        for hchunk in np.split(matrix, size):
            row_wise.append(np.split(hchunk, size, 1))
        return row_wise

    def sum_chunks(chunks):
        sum_rows = []
        for row in chunks:
            sum_rows.append([np.sum(col) for col in row])
        return np.array(sum_rows)

或者更紧凑的

    def sum_in_place(matrix, size):
        return np.array([[np.sum(vchunk) for vchunk in np.split(hchunk, size, 1)]
                         for hchunk in np.split(matrix, size)])

这将为您提供以下内容:

    In [16]: a
    Out[16]: 
    array([[ 0,  1,  2,  3],
           [ 4,  5,  6,  7],
           [ 8,  9, 10, 11],
           [12, 13, 14, 15]])

    In [17]: chunk.sum_in_place(a, 2)
    Out[17]: 
    array([[10, 18],
           [42, 50]])

【讨论】:

  • FWIW,我用 10000 x 10000 矩阵来计时,求和 5 x 5(到 2000 年减少到 2000),ipython 说 10 loops, best of 3: 113 ms per loop。我知道它比您所说的情况要小得多,但仍然足够大以至于不平凡——但它也是次优代码。你能解释一下什么是“足够快”吗?
【解决方案3】:

对于 4x4 示例,您可以执行以下操作:

In [43]: a = np.arange(16.).reshape((4, 4))
In [44]: a 
Out[44]: 
array([[  0.,   1.,   2.,   3.],
       [  4.,   5.,   6.,   7.],
       [  8.,   9.,  10.,  11.],
       [ 12.,  13.,  14.,  15.]])
In [45]: u = np.array([a[:2, :2], a[:2, 2:], a[2:,:2], a[2:, 2:]])
In [46]: u
Out[46]: 
array([[[  0.,   1.],
        [  4.,   5.]],

       [[  2.,   3.],
        [  6.,   7.]],

       [[  8.,   9.],
        [ 12.,  13.]],

       [[ 10.,  11.],
        [ 14.,  15.]]])

In [47]: u.sum(1).sum(1).reshape(2, 2)
Out[47]: 
array([[ 10.,  18.],
       [ 42.,  50.]])

使用itertools 之类的东西应该可以自动化和泛化u 的表达式。

【讨论】:

  • 感谢您的解决方案,但我认为对于非常大的矩阵是不切实际的。这就是我改用 scipy 稀疏矩阵的原因。
猜你喜欢
  • 2018-01-19
  • 2020-07-30
  • 2020-12-07
  • 2015-07-22
  • 1970-01-01
  • 1970-01-01
  • 2019-05-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多