在scipy.sparse矩阵上的布尔运算

12 投票
2 回答
7743 浏览
提问于 2025-04-17 13:35

我有一组稀疏矩阵,里面填满了布尔值(就是只有真和假),我需要对它们进行逻辑运算(主要是逐元素的“或”运算)。

就像在numpy中,两个布尔类型的矩阵相加时,会得到逐元素的“或”结果,但有一个麻烦的副作用:

>>> from scipy import sparse
>>> [a,b] = [sparse.rand(5,5,density=0.1,format='lil').astype('bool')
...  for x in range(2)]
>>> b
<5x5 sparse matrix of type '<class 'numpy.bool_'>'
    with 2 stored elements in LInked List format>
>>> a+b
<5x5 sparse matrix of type '<class 'numpy.int8'>'
    with 4 stored elements in Compressed Sparse Row format>

结果的数据类型会变成'int8',这会导致后续操作出现问题。虽然可以通过下面的方法来解决:

(a+b).astype('bool')

但我感觉这种类型的变化会影响性能。

为什么结果的数据类型和操作数的数据类型不一样呢?
有没有更好的方法在python中对稀疏矩阵进行逻辑运算?

2 个回答

7

你可以很简单地用以下方法来表示布尔运算。这样它就可以和稀疏矩阵一起使用。

a.multiply(b) #AND
a+b           #OR
(a>b)+(a<b)   #XOR
a>b           #NOT

所以,布尔运算被支持的。

6

稀疏矩阵不支持逻辑运算,但把它转换回布尔值其实并不太费事。实际上,如果你使用的是LIL格式的矩阵,转换的时间可能会因为性能波动而看起来是负的:

a = scipy.sparse.rand(10000, 10000, density=0.001, format='lil').astype('bool')
b = scipy.sparse.rand(10000, 10000, density=0.001, format='lil').astype('bool')

In [2]: %timeit a+b
10 loops, best of 3: 61.2 ms per loop

In [3]: %timeit (a+b).astype('bool')
10 loops, best of 3: 60.4 ms per loop

你可能注意到,在把LIL矩阵相加之前,它们被转换成了CSR格式,看看返回的格式。如果你一开始就使用CSR格式,那么转换的额外开销就会更明显:

In [14]: %timeit a+b
100 loops, best of 3: 2.28 ms per loop

In [15]: %timeit (a+b).astype(bool)
100 loops, best of 3: 2.96 ms per loop

CSR(和CSC)矩阵有一个叫做data的属性,它是一个一维数组,里面存放着稀疏矩阵中实际的非零元素。因此,重新转换你的稀疏矩阵的成本将取决于矩阵中非零元素的数量,而不是它的大小:

a = scipy.sparse.rand(10000, 10000, density=0.0005, format='csr').astype('int8')
b = scipy.sparse.rand(1000, 1000, density=0.5, format='csr').astype('int8')

In [4]: %timeit a.astype('bool') # a is 10,000x10,000 with 50,000 non-zero entries
10000 loops, best of 3: 93.3 us per loop

In [5]: %timeit b.astype('bool') # b is 1,000x1,000 with 500,000 non-zero entries
1000 loops, best of 3: 1.7 ms per loop

撰写回答