【问题标题】:python & numpy: sum of an array slicepython & numpy:数组切片的总和
【发布时间】:2011-05-05 00:53:28
【问题描述】:

我有一个一维 numpy 数组 (array_) 和一个 Python 列表 (list_)。

以下代码有效,但效率低下,因为切片涉及不必要的副本(当然适用于 Python 列表,我相信也适用于 numpy 数组?):

result = sum(array_[1:])
result = sum(list_[1:])

有什么好的方法来重写它?

【问题讨论】:

    标签: python arrays numpy


    【解决方案1】:

    对 numpy 数组进行切片不会像在列表的情况下那样制作副本。

    作为一个基本示例:

    import numpy as np
    x = np.arange(100)
    y = x[1:5]
    y[:] = 1000
    print x[:10]
    

    这会产生:

    [   0 1000 1000 1000 1000    5    6    7    8    9]
    

    尽管我们修改了y 中的值,但它只是对与x 相同内存的视图。

    切片 ndarray 会返回一个视图并且不会复制内存。

    但是,使用array_[1:].sum() 比在numpy 数组上调用python 的内置sum 效率更高。

    作为一个快速比较:

    In [28]: x = np.arange(10000)
    
    In [29]: %timeit x.sum()
    100000 loops, best of 3: 10.2 us per loop
    
    In [30]: %timeit sum(x)
    100 loops, best of 3: 4.01 ms per loop
    

    编辑:

    在列表的情况下,如果由于某种原因您不想复制,您可以随时使用itertools.islice。而不是:

    result = sum(some_list[1:])
    

    你可以这样做:

    result = sum(itertools.islice(some_list, 1, None))
    

    不过,在大多数情况下,这太过分了。如果您处理的列表足够长以至于内存管理是一个主要问题,那么您可能不应该使用列表来存储您的值。 (列表并非旨在或旨在将项目紧凑地存储在内存中。)

    此外,您不希望对 numpy 数组执行此操作。简单地执行some_array[1:].sum() 将快几个数量级,并且不会使用比islice 更多的内存任何

    【讨论】:

    • 你的机器一定有什么奇怪的东西。根据我的时间安排,sum(x) 只比x.sum() 慢 5-10%(在numpy 1.5.1 上)。谢谢
    • @eat - 您使用的是相对较大(>1000 个元素)的数组吗?对于短(约 10 个元素)数组, sum 只慢了几个百分点,但是当您获得 >1000 个元素时,这只是微秒与毫秒的问题。我已经在 3 台具有不同硬件和不同版本的 python 和 numpy 的机器上运行它。时间不同,但缩放结果相似。它们都是线性时间算法,但是在 python 中遍历 numpy 数组的每个元素(这是 sum 所做的)比遍历 C 中的内存缓冲区的每个元素(这是 numpy 所做的)要慢得多跨度>
    • 不,和你的一样大。我会滥用答案一段时间以显示我的时间安排。谢谢
    • 你是如何从 shell 中调用 [29] 和 [30] 中的命令行 timeit 的?你启动了一个子shell吗?
    • @max - 这是 IPython 的“神奇”函数之一。 ipython.scipy.org/moin 将完整的timeit.timeit 调用与设置等一起使用更方便......它所做的只是使用以前在交互式shell 中运行的代码作为setup kwarg 到timeit.timeit。如果您还没有使用 IPython,请查看它!它有很多不错的小功能和宏!
    【解决方案2】:

    当谈到列表时,我的第一直觉与 Joe Kington 相同,但我检查了一下,至少在我的机器上,islice 始终较慢!

    >>> timeit.timeit("sum(l[50:950])", "l = range(1000)", number=10000)
    1.0398731231689453
    >>> timeit.timeit("sum(islice(l, 50, 950))", "from itertools import islice; l = range(1000)", number=10000)
    1.2317550182342529
    >>> timeit.timeit("sum(l[50:950000])", "l = range(1000000)", number=10)
    7.9020509719848633
    >>> timeit.timeit("sum(islice(l, 50, 950000))", "from itertools import islice; l = range(1000000)", number=10)
    8.4522969722747803
    

    我尝试了custom_sum,发现它更快,但不是很多:

    >>> setup = """
    ... def custom_sum(list, start, stop):
    ...     s = 0
    ...     for i in xrange(start, stop):
    ...         s += list[i]
    ...     return s
    ... 
    ... l = range(1000)
    ... """
    >>> timeit.timeit("custom_sum(l, 50, 950)", setup, number=1000)
    0.66767406463623047
    

    此外,如果数字越大,它的速度就越慢!

    >>> setup = setup.replace("range(1000)", "range(1000000)")
    >>> timeit.timeit("custom_sum(l, 50, 950000)", setup, number=10)
    14.185815095901489
    

    我想不出还有什么要测试的。 (想法,有人吗?)

    【讨论】:

    • 好吧,我并没有声称它更快(没有理由会这样)。 (不过,我对它的速度有多慢感到惊讶......)我声称它会使用更少的内存,因为它不会复制列表。它确实做到了。
    • @Joe,抱歉,我以为你在谈论速度。
    • 好吧,重新阅读我的帖子,确实是这样!我的意思是内存使用情况,但从我写的内容中并不清楚。我对有多大的差异很感兴趣...奇怪的是,如果列表足够大,islice 似乎又变得更快了...不过,您需要 ~1e7 个元素。我想那时分配新内存会成为瓶颈。无论如何,您的帖子肯定是一个有趣的比较!
    【解决方案3】:

    @Joe Kington(这是显示我的时间安排的临时答案,我会尽快删除它):

    In []: x= arange(1e4)
    In []: %timeit sum(x)
    100000 loops, best of 3: 18.8 us per loop
    In []: %timeit x.sum()
    100000 loops, best of 3: 17.5 us per loop
    In []: x= arange(1e5)
    In []: %timeit sum(x)
    10000 loops, best of 3: 165 us per loop
    In []: %timeit x.sum()
    10000 loops, best of 3: 158 us per loop
    In []: x= arange(1e2)
    In []: %timeit sum(x)
    100000 loops, best of 3: 4.44 us per loop
    In []: %timeit x.sum()
    100000 loops, best of 3: 3.2 us per loop
    

    据我的 numpy(1.5.1) 消息来源所说,sum(.) 只是 x.sum(.) 的包装。因此对于较大的输入,sum(.)x.sum(.) 的执行时间相同(渐近)。

    编辑:这个答案只是一个临时答案,但实际上它(及其 cmets)可能确实对某人有用。所以我就让它保持原样,直到有人真的要求我删除它。

    【讨论】:

    • @eat:我认为 OP 的代码和 Joe 的计时使用内置的 sum() 而不是 numpy.sum()。这只是使用from numpy import * 的陷阱之一。
    • @eat - 是的,我故意将 python 的 sum 与 numpy 的 sum 进行比较。我假设 OP 也指的是 python 的sum。 (他很可能不是......)我必须同意@Sven,在这里......这正是from whatever import *不好的原因,以及为什么在像numpy一样大的命名空间的情况下它特别糟糕! :)
    • 啊哈,好吧,这(有点)有道理。然而,我个人觉得使用IPython 个人资料scipy 工作很舒服,即使它与pythons sum(.) 一起工作也很难。 (而且由于乔也使用IPython,我只是误解了时间)。顺便说一句,我还是应该删除这个答案还是留下它(用 cmets 作为对 OP 问题的进一步澄清)?谢谢
    • 嗯,我认为它澄清了其他人可能会感到困惑的一点。如果是我,我会留下它......当然,你的电话。
    • @eat: from numpy import * 阴影 all(), min(), max(), sum(), any(), abs()round()。对我来说最大的阻碍(即使是交互式使用)是any()all()——numpy 版本不适用于生成器表达式。 (我也建议不要删除这个答案。)
    【解决方案4】:

    我没有发现x[1:].sum() 明显比x.sum() 慢。对于列表,sum(x) - x[0]sum(x[1:]) 快​​(OMM 快大约 40%)。

    【讨论】:

      猜你喜欢
      • 2020-12-11
      • 2011-08-30
      • 1970-01-01
      • 2021-04-21
      • 2019-07-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多