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

为什么lil_matrix和dok_matrix与普通的dict相比这么慢?

梁丘钊
2023-03-14
问题内容

我想迭代地构建稀疏矩阵,并注意到根据SciPy文档,有两种合适的选择:

LiL矩阵:

类scipy.sparse.lil_matrix(arg1,shape = None,dtype = None,copy =
False)[源]基于行的链表稀疏矩阵

这是用于增量构造稀疏矩阵的有效结构。

DoK矩阵:

类scipy.sparse.dok_matrix(arg1,shape = None,dtype = None,copy =
False)[源]基于密钥字典的稀疏矩阵。

这是用于增量构造稀疏矩阵的有效结构。

但是,当我运行基准测试与构建值字典字典(以后可以轻松地将其转换为稀疏矩阵)进行比较时,后者的速度比使用任何稀疏矩阵模型快10到20倍:

from scipy.sparse import dok_matrix, lil_matrix
from timeit import timeit
from collections import defaultdict

def common_dict(rows, cols):
    freqs = defaultdict(lambda: defaultdict(int))
    for row, col in zip(rows, cols):
        freqs[row][col] += 1

    return freqs

def dok(rows, cols):
    freqs = dok_matrix((1000,1000))
    for row, col in zip(rows, cols):
        freqs[row,col] += 1

    return freqs

def lil(rows, cols):
    freqs = lil_matrix((1000,1000))
    for row, col in zip(rows, cols):
        freqs[row,col] += 1

    return freqs


def benchmark():
    cols = range(1000)
    rows = range(1000)

    res = timeit("common_dict({},{})".format(rows, cols), 
                 "from __main__ import common_dict", 
                 number=100)

    print("common_dict: {}".format(res))

    res = timeit("dok({},{})".format(rows, cols), 
                 "from __main__ import dok", 
                 number=100)

    print("dok: {}".format(res))

    res = timeit("lil({},{})".format(rows, cols), 
                 "from __main__ import lil", 
                 number=100)

    print("lil: {}".format(res))

结果:

benchmark()

common_dict: 0.11778324202168733
dok: 2.2927695910912007
lil: 1.3541790939634666

是什么引起矩阵模型的这种开销,并且有某种方法可以加快它的速度?在某些情况下,dok或lil比普通的dict更喜欢吗?


问题答案:

当我将您更改+=为仅=用于2个稀疏数组时:

for row, col in zip(rows, cols):
    #freqs[row,col] += 1
    freqs[row,col] = 1

他们各自的时间减少了一半。消耗时间最多的是索引。使用+=它必须同时执行a__getitem__和a __setitem__

当文档这么说dok并且lil更适合迭代构造时,它们意味着扩展其基础数据结构比其他格式更容易。

当我尝试csr用您的代码创建矩阵时,我得到:

/usr/lib/python2.7/dist-
packages/scipy/sparse/compressed.py:690:SparseEfficiencyWarning:更改csr_matrix的稀疏结构非常昂贵。lil_matrix效率更高。稀疏效率警告)

速度降低30倍。

因此,速度要求与诸如之类的格式有关csr,而不与纯Python或numpy结构有关。

您可能想要查看Python代码,dok_matrix.__get_item__dok_matrix.__set_item__查看这样做时会发生什么freq[r,c]

一种更快的构造方法dok是:

freqs = dok_matrix((1000,1000))
d = dict()
for row, col in zip(rows, cols):
    d[(row, col)] = 1
freqs.update(d)

利用adok是子类字典的事实。请注意,dok矩阵不是字典的字典。它的键是元组之类的(50,50)

构造相同的稀疏数组的另一种快速方法是:

freqs = sparse.coo_matrix((np.ones(1000,int),(rows,cols)))

换句话说,由于您已经具有rowsandcols数组(或范围),因此请计算相应的data数组,然后构造稀疏数组。

但是,如果您必须在增量增长步骤之间对矩阵执行稀疏运算,那么dok或者lil可能是您的最佳选择。

开发了用于解决线性代数问题的稀疏矩阵,例如使用大型稀疏矩阵求解线性方程。几年前,我在MATLAB中使用它们来解决有限差分问题。对于这项工作,计算友好的csr格式是最终目标,而该coo格式是一种方便的初始化格式。

现在,许多SO稀疏问题都来自scikit- learn文本分析问题。它们还用于生物学数据库文件中。但是(data),(row,col)定义方法仍然效果最好。

因此,稀疏矩阵从未打算用于快速增量创建。字典和列表之类的传统Python结构对此要好得多。

dok是利用其字典方法的更快迭代。
update似乎和普通字典一样快。get大约是等效索引(freq[row,col])的3倍。索引可能使用get,但必须有很多开销。

def fast_dok(rows, cols):
    freqs = dok_matrix((1000,1000))
    for row, col in zip(rows,cols):
         i = freqs.get((row,col),0)
         freqs.update({(row,col):i+1})
    return freqs

跳过get,然后开始

         freqs.update({(row,col): 1)

甚至更快-比defaultdict示例的defaultdict更快,几乎与简单的字典初始化({(r, c):1 for r,c in zip(rows,cols)})一样快



 类似资料:
  • 问题内容: 从2010年的计算机语言基准游戏中可以看出: Go平均比C慢10倍 Go比Java慢3倍! 考虑到Go编译器会生成要执行的本机代码,这怎么可能? Go的编译器不成熟?还是Go语言存在一些内在问题? 编辑: 大多数答案否认Go语言的内在缓慢,声称问题出在不成熟的编译器中。 因此,我进行了一些自己的测试来计算斐波那契数:迭代算法在Go(freebsd,6g)中以与C(带有O3选项)一样的速

  • 问题内容: 这里是Spark新手。我最近开始使用以下命令在两个内核的本地计算机上使用Spark: 我有一个393Mb的文本文件,其中包含近一百万行。我想执行一些数据操作操作。我使用的是内置PySpark的数据帧的功能进行简单的操作,如,,,。 但是,当我在完全相同的数据集上对熊猫进行完全相同的操作时,就延迟而言,熊猫似乎在很大程度上击败了pyspark。 我想知道这可能是什么原因。我有几点想法。

  • Kotlin代码是这样的: 可以简单地更改为 我知道同伴对象可以用来作为真正的java静态函数使用,但是使用同伴对象还有其他的优点吗?

  • 为什么我的SIMD向量4长度函数比原始向量长度方法慢3倍? SIMD矢量4长度函数: 幼稚的实现: 我用GCC(Ubuntu7.4.0-1Ubuntu1~18.04.1)7.4.0: SSE版本输出: 纯C版本输出:

  • 本文向大家介绍唯一索引比普通索引快吗, 为什么?相关面试题,主要包含被问及唯一索引比普通索引快吗, 为什么?时的应答技巧和注意事项,需要的朋友参考一下 唯一索引不一定比普通索引快, 还可能慢. 查询时, 在未使用limit 1的情况下, 在匹配到一条数据后, 唯一索引即返回, 普通索引会继续匹配下一条数据, 发现不匹配后返回. 如此看来唯一索引少了一次匹配, 但实际上这个消耗微乎其微. 更新时,

  • 我正在尝试线性逻辑中的一个例子。 它首先介绍了定义了常规操作的标准阵列(第24页): 然后建议线性等价物(使用类型签名的线性逻辑来限制数组复制)将具有稍微不同的类型签名: 这是基于这样的理念设计的,即数组包含的值复制起来很便宜,但数组本身复制起来很昂贵,因此应该作为句柄从使用传递到使用。 问题:查找和更新的签名与标准签名很好地对应,但是我如何解释新签名? 特别地: 函数new似乎没有返回数组。如果