【发布时间】:2011-05-05 00:53:28
【问题描述】:
我有一个一维 numpy 数组 (array_) 和一个 Python 列表 (list_)。
以下代码有效,但效率低下,因为切片涉及不必要的副本(当然适用于 Python 列表,我相信也适用于 numpy 数组?):
result = sum(array_[1:])
result = sum(list_[1:])
有什么好的方法来重写它?
【问题讨论】:
我有一个一维 numpy 数组 (array_) 和一个 Python 列表 (list_)。
以下代码有效,但效率低下,因为切片涉及不必要的副本(当然适用于 Python 列表,我相信也适用于 numpy 数组?):
result = sum(array_[1:])
result = sum(list_[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 上)。谢谢
timeit.timeit 调用与设置等一起使用更方便......它所做的只是使用以前在交互式shell 中运行的代码作为setup kwarg 到timeit.timeit。如果您还没有使用 IPython,请查看它!它有很多不错的小功能和宏!
当谈到列表时,我的第一直觉与 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
我想不出还有什么要测试的。 (想法,有人吗?)
【讨论】:
islice 似乎又变得更快了...不过,您需要 ~1e7 个元素。我想那时分配新内存会成为瓶颈。无论如何,您的帖子肯定是一个有趣的比较!
@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)可能确实对某人有用。所以我就让它保持原样,直到有人真的要求我删除它。
【讨论】:
sum() 而不是 numpy.sum()。这只是使用from numpy import * 的陷阱之一。
sum 与 numpy 的 sum 进行比较。我假设 OP 也指的是 python 的sum。 (他很可能不是......)我必须同意@Sven,在这里......这正是from whatever import *不好的原因,以及为什么在像numpy一样大的命名空间的情况下它特别糟糕! :)
IPython 个人资料scipy 工作很舒服,即使它与pythons sum(.) 一起工作也很难。 (而且由于乔也使用IPython,我只是误解了时间)。顺便说一句,我还是应该删除这个答案还是留下它(用 cmets 作为对 OP 问题的进一步澄清)?谢谢
from numpy import * 阴影 all(), min(), max(), sum(), any(), abs() 和 round()。对我来说最大的阻碍(即使是交互式使用)是any() 和all()——numpy 版本不适用于生成器表达式。 (我也建议不要删除这个答案。)
我没有发现x[1:].sum() 明显比x.sum() 慢。对于列表,sum(x) - x[0] 比 sum(x[1:]) 快(OMM 快大约 40%)。
【讨论】: