当前位置: 首页 > 面试题库 >

为什么scipy csr矩阵的行索引比numpy数组慢

华欣荣
2023-03-14
问题内容

我不确定自己在做什么错,但csr_matrix与numpy数组相比,对scipy进行行索引似乎要慢大约2倍(请参见下面的代码)。

csr矩阵的行索引是否应该比稠密矩阵快,因为像下面的情况一样,只有很少的非零元素被提取出来?

是否有技巧使scipy csr矩阵的行索引更快?

import numpy as np
import timeit
from scipy.sparse import csr_matrix

# Generate random matrix
A = np.random.rand(5000, 1000)

# Make A sparse
A[:, 4:] =0

# Create sparse matrix
A_sparse = csr_matrix(A)

# Create row indexing functions
def index_A_dense():
    A[4]

def index_A_dense_copy():
    a = A[4].copy()

def index_A_sparse():
    A_sparse[4]

timeit.timeit(index_A_sparse, number=10000)
Out: 0.6668063667304978
timeit.timeit(index_A_dense, number=10000)
Out: 0.0029007720424942818
timeit.timeit(index_A_dense_copy, number=10000)
Out: 0.00979283193282754

提前致谢!


问题答案:

我在下面演示的简短答案是,构造新的稀疏矩阵非常昂贵。开销很大,不依赖于行数或特定行中非零元素的数量。

稀疏矩阵的数据表示形式与密集阵列的数据表示形式完全不同。数组将数据存储在一个连续的缓冲区中,并有效地使用shapestrides迭代选定的值。这些值加上索引定义了将在缓冲区中找到数据的确切位置。将这些N字节从一个位置复制到另一个是整个操作中相对较小的部分。

稀疏矩阵将数据存储在几个包含索引和数据的数组(或其他结构)中。然后,选择一行需要查找相关索引,并使用选定的索引和数据构建一个新的稀疏矩阵。稀疏包中有已编译的代码,但是底层代码不如numpy数组那么多。

为了说明这一点,我将制作一个小的矩阵,而不是那么密集,所以我们没有很多空行:

In [259]: A = (sparse.rand(5,5,.4,'csr')*20).floor()
In [260]: A
Out[260]: 
<5x5 sparse matrix of type '<class 'numpy.float64'>'
    with 10 stored elements in Compressed Sparse Row format>

密集等效项,以及一个行副本:

In [262]: Ad=A.A
In [263]: Ad
Out[263]: 
array([[  0.,   0.,   0.,   0.,  10.],
       [  0.,   0.,   0.,   0.,   0.],
       [ 17.,  16.,  14.,  19.,   6.],
       [  0.,   0.,   1.,   0.,   0.],
       [ 14.,   0.,   9.,   0.,   0.]])
In [264]: Ad[4,:]
Out[264]: array([ 14.,   0.,   9.,   0.,   0.])
In [265]: timeit Ad[4,:].copy()
100000 loops, best of 3: 4.58 µs per loop

矩阵行:

In [266]: A[4,:]
Out[266]: 
<1x5 sparse matrix of type '<class 'numpy.float64'>'
    with 2 stored elements in Compressed Sparse Row format>

查看此csr矩阵(3个1d数组)的数据表示形式:

In [267]: A.data
Out[267]: array([  0.,  10.,  17.,  16.,  14.,  19.,   6.,   1.,  14.,   9.])  
In [268]: A.indices
Out[268]: array([3, 4, 0, 1, 2, 3, 4, 2, 0, 2], dtype=int32)
In [269]: A.indptr
Out[269]: array([ 0,  2,  2,  7,  8, 10], dtype=int32)

这是选择行的方式(但在已编译的代码中):

In [270]: A.indices[A.indptr[4]:A.indptr[5]]
Out[270]: array([0, 2], dtype=int32)
In [271]: A.data[A.indptr[4]:A.indptr[5]]
Out[271]: array([ 14.,   9.])

“行”是另一个稀疏矩阵,具有相同类型的数据数组:

In [272]: A[4,:].indptr
Out[272]: array([0, 2])
In [273]: A[4,:].indices
Out[273]: array([0, 2])
In [274]: timeit A[4,:]

是的,稀疏矩阵的时序很慢。我不知道实际选择数据花费了多少时间,以及构造新矩阵花费了多少时间。

10000 loops, best of 3: 145 µs per loop
In [275]: timeit Ad[4,:].copy()
100000 loops, best of 3: 4.56 µs per loop

lil 格式可能更容易理解,因为数据和索引存储在子列表中,每行一个。

In [276]: Al=A.tolil()
In [277]: Al.data
Out[277]: array([[0.0, 10.0], [], [17.0, 16.0, 14.0, 19.0, 6.0], [1.0], [14.0, 9.0]], dtype=object)
In [278]: Al.rows
Out[278]: array([[3, 4], [], [0, 1, 2, 3, 4], [2], [0, 2]], dtype=object)
In [279]: Al[4,:].data
Out[279]: array([[14.0, 9.0]], dtype=object)
In [280]: Al[4,:].rows
Out[280]: array([[0, 2]], dtype=object)

这样的速度比较在处理紧密的编译代码时很有意义,在这种情况下,字节从内存的一部分到另一部分的移动是大量的时间消耗者。结合使用Python和已编译的代码numpyscipy您不仅可以计算O(n)操作数。

============================

这是从中选择行所需的时间A,以及返回新的稀疏矩阵所需的时间:

只需获取数据:

In [292]: %%timeit
d1=A.data[A.indptr[4]:A.indptr[5]]
i1=A.indices[A.indptr[4]:A.indptr[5]]
   .....: 
100000 loops, best of 3: 4.92 µs per loop

加上制作矩阵所需的时间:

In [293]: %%timeit
d1=A.data[A.indptr[4]:A.indptr[5]]
i1=A.indices[A.indptr[4]:A.indptr[5]]
sparse.csr_matrix((d1,([0,0],i1)),shape=(1,5))
   .....: 
1000 loops, best of 3: 445 µs per loop

尝试更简单的coo矩阵

In [294]: %%timeit
d1=A.data[A.indptr[4]:A.indptr[5]]
i1=A.indices[A.indptr[4]:A.indptr[5]]
sparse.coo_matrix((d1,([0,0],i1)),shape=(1,5))
   .....: 
10000 loops, best of 3: 135 µs per loop


 类似资料:
  • 本文向大家介绍MATLAB索引矩阵和数组,包括了MATLAB索引矩阵和数组的使用技巧和注意事项,需要的朋友参考一下 示例 MATLAB允许使用几种方法来索引(访问)矩阵和数组的元素: 下标索引-您可以在其中分别指定所需元素在矩阵每个维度中的位置。 线性索引-将矩阵视为向量,无论其尺寸如何。这意味着,您可以用一个数字指定矩阵中的每个位置。 逻辑索引-在其中使用逻辑矩阵(以及true和false值的矩

  • 问题内容: 我正在尝试创建具有混合数据类型(字符串,整数,整数)的NumPy数组/矩阵(Nx3)。但是,当我通过添加一些数据来添加此矩阵时,出现错误: TypeError:无效的类型提升 。拜托,有人可以帮我解决这个问题吗? 当我用示例数据创建一个数组时,NumPy将矩阵中的所有列都转换为一种“ S”数据类型。而且我无法为数组指定数据类型,因为当我执行此操作时, res = np.array([“

  • C++:15秒(源) Python:6分13秒(来源) C++:45分钟(源) 蟒蛇:10小时后被杀死(来源) 为什么Strassen矩阵乘法比标准矩阵乘法慢得多? null null null

  • 问题内容: 我倾向于用括号为numpy数组(矩阵)建立索引,但是当我想对数组(矩阵)进行切片时,我注意到我必须使用逗号表示法。为什么是这样?例如, 问题答案: 这个: 表示“沿第一个轴获取所有索引,但沿第二个轴仅获取索引1”。 这个: 意思是“沿第一个轴获取所有索引(所以的全部),然后沿结果的 第一个 轴获取索引1 ”。您将应用于错误的轴。 并且仅是等效的,因为使用整数对数组进行索引会将所有其余轴

  • 问题内容: 当以某种出乎意料的方式执行特定切片时,numpy数组的形状正在改变 我尝试了将同一阵列切成薄片的几种方法,但是细微的差异会导致阵列形状的结果不同 在最后两个示例中,我不明白为什么最后一个轴移动到第一个位置。 我使用与 问题答案: 在最后两种情况下可能会发现结果出乎意料的原因是,即使您也使用切片进行索引,但数组的索引仍遵循高级索引的规则。 有关此行为的详细说明,您可以检查结合使用高级索引

  • 问题内容: 我有2个形状(5,1)的numpy数组,说:a = [1,2,3,4,5] b = [2,4,2,3,6] 我如何制作一个矩阵,将每个第i个元素与每个第j个元素相乘?喜欢: 不使用forloops?我可以使用重塑,缩小或乘法的任何组合吗? 现在,我沿着行和列创建每个数组的aa * b拼接,然后将元素明智地相乘,但是在我看来,肯定有一种更简单的方法。 问题答案: 使用numpy.oute

  • 问题内容: 我正在使用Numpy将数据存储到矩阵中。从R背景开始,有一种极其简单的方法将函数应用于矩阵的行/列或两者。 python / numpy组合是否有类似的东西?编写自己的小实现不是问题,但是在我看来,我想出的大多数版本都将比现有的实现效率低得多/占用更多内存。 我想避免从numpy矩阵复制到局部变量等,这可能吗? 我尝试实现的功能主要是简单的比较(例如,某列中有多少个元素小于数字x,或者

  • 在不同大小的方阵上进行了一些实验后,一个模式出现了。对大小为的矩阵进行转置总是比对大小为的矩阵进行转置慢。对于的小值,差异不大。 但是,如果值为512,则会出现很大的差异。(至少对我来说) 免责声明:我知道函数实际上并没有转置矩阵,因为元素的双重交换,但这没有区别。 null null