Python 如何有效地从较大的稀疏矩阵的块和中生成新矩阵
我有一个大的scipy稀疏对称矩阵,我需要通过求块的和来压缩它,从而生成一个新的更小的矩阵 例如,对于4x4稀疏矩阵a,我想制作一个2x2矩阵B,其中B[I,j]=和(a[I:I+2,j:j+2]) 目前,我只是逐块重新创建压缩矩阵,但速度很慢。关于如何优化这一点有什么想法吗 更新:下面是一个示例代码,它运行良好,但对于50.000x50.000的稀疏矩阵来说速度较慢,我想将其压缩为10.000x10.000:Python 如何有效地从较大的稀疏矩阵的块和中生成新矩阵,python,matrix,scipy,sparse-matrix,Python,Matrix,Scipy,Sparse Matrix,我有一个大的scipy稀疏对称矩阵,我需要通过求块的和来压缩它,从而生成一个新的更小的矩阵 例如,对于4x4稀疏矩阵a,我想制作一个2x2矩阵B,其中B[I,j]=和(a[I:I+2,j:j+2]) 目前,我只是逐块重新创建压缩矩阵,但速度很慢。关于如何优化这一点有什么想法吗 更新:下面是一个示例代码,它运行良好,但对于50.000x50.000的稀疏矩阵来说速度较慢,我想将其压缩为10.000x10.000: >>> A = (rand(4,4)<0.3)*rand(4
>>> 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()
A=(rand(4,4)>>A=scipy.sparse.lil_矩阵(A+A.T)#使矩阵对称
>>>B=scipy.sparse.lil_矩阵((2,2))
>>>对于范围内的i(B.形状[0]):
…对于范围内的j(B.形状[0]):
…B[i,j]=A[i:i+2,j:j+2].sum()
对于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.]])
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]])
如果给定一个大小为N的方阵和一个大小为d的拆分矩阵(因此矩阵将被划分为大小为d的N/d*N/d子矩阵),那么使用类似的方法,应该可以自动化和泛化
u
的表达式,您可以使用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 [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.]])
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]])
首先,
lil
矩阵对于您总结的矩阵来说可能非常糟糕,我会尝试COO
或者CSR/CSS
(我不知道哪一个更好,但是lil
对于这些操作中的许多操作来说可能天生就比较慢,即使切片也可能慢很多,尽管我没有测试)。(除非您知道,例如dia
非常适合)
基于COO
我可以想象做一些恶作剧。因为COO
有row
和col
数组来给出准确的位置:
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)))
(我不能保证我没有在某个地方混淆行和列,这只适用于方阵…你试过使用numpy的
A[I:I+2,j:j+2].sum()吗
而不是python的求和
?如何确定块大小?它是固定的吗?@wim实际上我使用的是稀疏矩阵求和。@NPE块大小不是固定的。是大矩阵大小的除数。感谢您的解决方案,但我认为对于非常大的矩阵是不切实际的。这就是为什么我使用scipy稀疏矩阵。FWIW,我用10000 x 10000个矩阵计时,求和5 x 5(到2000年减少到2000),Ipyton说10个循环,每个循环最好3:113 ms
。我知道它比你所说的情况小得多,但仍然足够大,不平凡,但它也是次优代码。你能告诉我什么是“足够快”吗是吗?哇,这是一个很棒的解决方案!谢谢!我不知道np.bincount