为什么 CSR 格式在选择行方面优于 CSC 格式?
Why CSR format outperforms CSC format in selecting rows?
我正在基于 scipy.sparse
在 CSR 和 CSC 格式上做这个小实验。正如 documentation 声称的那样,在处理列操作时,CSC 格式比 CSR 格式更有效。所以我做了这个小实验:
from scipy.sparse import diags
d = diags([-1, 1, 1], [-1, 0, -1], shape= (100, 100))
%timeit d.tocsc()[:, 1]
%timeit d.tocsr()[:, 1]
然后我想我以错误的方式得到了行和列。所以我尝试了另一种方法:
%timeit d.tocsc()[1]
%timeit d.tocsr()[1]
但在某些情况下,CSR 在时间上优于 CSC。我想知道这样的结果是否有任何结构性原因,或者我的测试只是有偏见?
稀疏矩阵的索引很复杂,有许多变量会影响时间。您的测试用例是对称的,因此 csr
和 csc
格式几乎相同。如果形状是矩形,或者布局不同,情况可能会有所不同。
标量、切片和列表的索引也可能不同。
请记住,无论格式如何,稀疏索引都比 numpy.ndarray
慢得多。如果需要迭代,尽量不要使用稀疏。 (如果您必须迭代,请考虑直接使用 'raw' 稀疏属性。)
In [64]: d = sparse.diags([-1, 1, 1], [-1, 0, 1], shape= (100, 100))
In [65]: d
Out[65]:
<100x100 sparse matrix of type '<class 'numpy.float64'>'
with 298 stored elements (3 diagonals) in DIAgonal format>
In [66]: %timeit d.tocsr()[:,1]
422 µs ± 13.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [67]: %timeit d.tocsc()[:,1]
506 µs ± 5.89 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [68]: %timeit d.tocsc()[1,:]
506 µs ± 1.15 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
将转换与索引分开:
In [69]: %%timeit x=d.tocsr()
...: x[:,1]
121 µs ± 2.05 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [70]: %%timeit x=d.tocsr()
...: x[1,:]
118 µs ± 2.95 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [71]: %%timeit x=d.tocsc()
...: x[:,1]
290 µs ± 5.89 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [72]: %%timeit x=d.tocsc()
...: x[1,:]
297 µs ± 14.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
我有点惊讶 csr
时间一直更快。但只是一点点。 csc
和 csr
使用共同的 compressed
基础 class (sparse.compressed._cs_matrix
),但编码细节似乎有利于 csr
.
===
为了好玩,索引一个 lil
格式
In [73]: %%timeit x=d.tolil()
...: x[1,:]
76.4 µs ± 231 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [74]: %%timeit x=d.tolil()
...: x[:,1]
118 µs ± 647 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
正如 lil
存储所预期的那样,行索引速度更快,尽管列索引时间还不错。
lil
有一个 getrow
,它是 sparse
中最接近 numpy
视图的东西:
In [75]: %%timeit x=d.tolil()
...: x.getrow(1)
36.7 µs ± 233 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
===
密集数组索引,即使转换时间更快:
In [83]: %%timeit x=d.A
...: x[:,1]
277 ns ± 9.97 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [84]: timeit d.A[:,1]
197 µs ± 587 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
我正在基于 scipy.sparse
在 CSR 和 CSC 格式上做这个小实验。正如 documentation 声称的那样,在处理列操作时,CSC 格式比 CSR 格式更有效。所以我做了这个小实验:
from scipy.sparse import diags
d = diags([-1, 1, 1], [-1, 0, -1], shape= (100, 100))
%timeit d.tocsc()[:, 1]
%timeit d.tocsr()[:, 1]
然后我想我以错误的方式得到了行和列。所以我尝试了另一种方法:
%timeit d.tocsc()[1]
%timeit d.tocsr()[1]
但在某些情况下,CSR 在时间上优于 CSC。我想知道这样的结果是否有任何结构性原因,或者我的测试只是有偏见?
稀疏矩阵的索引很复杂,有许多变量会影响时间。您的测试用例是对称的,因此 csr
和 csc
格式几乎相同。如果形状是矩形,或者布局不同,情况可能会有所不同。
标量、切片和列表的索引也可能不同。
请记住,无论格式如何,稀疏索引都比 numpy.ndarray
慢得多。如果需要迭代,尽量不要使用稀疏。 (如果您必须迭代,请考虑直接使用 'raw' 稀疏属性。)
In [64]: d = sparse.diags([-1, 1, 1], [-1, 0, 1], shape= (100, 100))
In [65]: d
Out[65]:
<100x100 sparse matrix of type '<class 'numpy.float64'>'
with 298 stored elements (3 diagonals) in DIAgonal format>
In [66]: %timeit d.tocsr()[:,1]
422 µs ± 13.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [67]: %timeit d.tocsc()[:,1]
506 µs ± 5.89 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [68]: %timeit d.tocsc()[1,:]
506 µs ± 1.15 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
将转换与索引分开:
In [69]: %%timeit x=d.tocsr()
...: x[:,1]
121 µs ± 2.05 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [70]: %%timeit x=d.tocsr()
...: x[1,:]
118 µs ± 2.95 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [71]: %%timeit x=d.tocsc()
...: x[:,1]
290 µs ± 5.89 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [72]: %%timeit x=d.tocsc()
...: x[1,:]
297 µs ± 14.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
我有点惊讶 csr
时间一直更快。但只是一点点。 csc
和 csr
使用共同的 compressed
基础 class (sparse.compressed._cs_matrix
),但编码细节似乎有利于 csr
.
===
为了好玩,索引一个 lil
格式
In [73]: %%timeit x=d.tolil()
...: x[1,:]
76.4 µs ± 231 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [74]: %%timeit x=d.tolil()
...: x[:,1]
118 µs ± 647 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
正如 lil
存储所预期的那样,行索引速度更快,尽管列索引时间还不错。
lil
有一个 getrow
,它是 sparse
中最接近 numpy
视图的东西:
In [75]: %%timeit x=d.tolil()
...: x.getrow(1)
36.7 µs ± 233 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
===
密集数组索引,即使转换时间更快:
In [83]: %%timeit x=d.A
...: x[:,1]
277 ns ± 9.97 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [84]: timeit d.A[:,1]
197 µs ± 587 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)