使用Python、NumPy和SciPy高效切片矩阵的矩阵乘法

4 投票
1 回答
2556 浏览
提问于 2025-04-17 13:24

我想把一个二维的 scipy.sparse.csr.csr_matrix(我们叫它 A)转换成一个二维的 numpy.ndarray(我们叫它 B)。

A 可能是这样的:

>shape(A)
(90, 10)

然后 B 应该是这样的:

>shape(B)
(9,10)

在这里,A 的每10行会被重新整理成一个新的值,也就是这10行中的最大值。由于稀疏矩阵的特殊性,列操作在这种不可哈希的类型上是无法工作的。我该如何通过矩阵乘法来得到这个 B 呢?

1 个回答

3

通过矩阵乘法,你可以高效地进行切片,创建一个“切片器”矩阵,在合适的位置放置1。被切片的矩阵会和“切片器”有相同的类型,这样你就可以有效地控制输出的类型。

下面你会看到一些比较,对于你的情况,最有效的方法是请求.A矩阵并进行切片。这种方法比.toarray()要快得多。当“切片器”作为ndarray创建后,使用乘法也是第二快的选择,先将其与csr矩阵相乘,然后对结果进行切片。

注意:使用coo格式的稀疏矩阵A会导致时间稍微变慢,但比例保持不变,并且sol3不适用,我后来发现乘法时会自动转换为csr格式。

import scipy
import scipy.sparse.csr as csr
test = csr.csr_matrix([
[11,12,13,14,15,16,17,18,19],
[21,22,23,24,25,26,27,28,29],
[31,32,33,34,35,36,37,38,39],
[41,42,43,44,45,46,47,48,49],
[51,52,53,54,55,56,57,58,59],
[61,62,63,64,65,66,67,68,69],
[71,72,73,74,75,76,77,78,79],
[81,82,83,84,85,86,88,88,89],
[91,92,93,94,95,96,99,98,99]])

def sol1():
    B = test.A[2:5]

def sol2():
    slicer = scipy.array([[0,0,0,0,0,0,0,0,0],
                          [0,0,0,0,0,0,0,0,0],
                          [0,0,1,0,0,0,0,0,0],
                          [0,0,0,1,0,0,0,0,0],
                          [0,0,0,0,1,0,0,0,0]])
    B = (slicer*test)[2:]
    return B

def sol3():
    B = (test[2:5]).A
    return B

def sol4():
    slicer = csr.csr_matrix( ((1,1,1),((2,3,4),(2,3,4))), shape=(5,9) )
    B = ((slicer*test).A)[2:] # just changing when we do the slicing
    return B

def sol5():
    slicer = csr.csr_matrix( ((1,1,1),((2,3,4),(2,3,4))), shape=(5,9) )
    B = ((slicer*test)[2:]).A
    return B


timeit sol1()
#10000 loops, best of 3: 60.4 us per loop

timeit sol2()
#10000 loops, best of 3: 91.4 us per loop

timeit sol3()
#10000 loops, best of 3: 111 us per loop

timeit sol4()
#1000 loops, best of 3: 310 us per loop

timeit sol5()
#1000 loops, best of 3: 363 us per loop

编辑:这个答案已经更新,将.toarray()替换为.A,这样得到了更快的结果,现在最好的解决方案都放在了最上面。

撰写回答