【问题标题】:Why CSR format outperforms CSC format in selecting rows?为什么 CSR 格式在选择行方面优于 CSC 格式?
【发布时间】:2019-04-28 23:45:46
【问题描述】:

我正在根据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。我想知道这样的结果是否有任何结构性原因,或者我的测试只是有偏见?

【问题讨论】:

    标签: python scipy sparse-matrix


    【解决方案1】:

    稀疏矩阵的索引很复杂,有许多变量会影响时间。您的测试用例是对称的,所以csrcsc 格式几乎相同。如果形状是矩形或布局不同,情况可能会有所不同。

    标量索引、切片索引和列表索引也可能不同。

    请记住,无论格式如何,稀疏索引都比numpy.ndarray 慢得多。如果需要迭代,尽量不要使用稀疏。 (如果您必须迭代,请考虑直接使用“原始”稀疏属性。)

    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 的时间总是更快。但只是一点点。 csccsr 使用通用的 compressed 基类 (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)
    

    【讨论】:

    • 谢谢,这真的很丰富!两个后续问题:不知何故,当我在笔记本电脑上测试时tolil 是三个中最慢的?因此,如果我的任务是在一个长循环中处理一个巨大的对角带状矩阵,我应该牺牲我的内存存储来提高效率吗? (在你看来)
    猜你喜欢
    • 2012-07-07
    • 2010-12-22
    • 1970-01-01
    • 1970-01-01
    • 2019-10-06
    • 1970-01-01
    • 2011-04-03
    • 2013-07-08
    • 2014-10-08
    相关资源
    最近更新 更多