python和numpy:数组切片的求和
我有一个一维的numpy数组(array_
)和一个Python列表(list_)。
下面的代码可以运行,但效率不高,因为切片操作会产生不必要的复制(对于Python列表来说肯定是这样,我觉得numpy数组也是如此):
result = sum(array_[1:])
result = sum(list_[1:])
有没有什么好的方法可以重写这个代码呢?
4 个回答
@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(.)
的执行时间是差不多的(从大体上看)。
编辑:这个回答本来只是临时的,但实际上它(以及评论)可能对某些人有用。所以我就先不删了,直到有人真的要求我删除它。
我最初的想法和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
我想不出其他可以测试的东西了。(有没有人有什么想法?)
切片一个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
占用更多的内存。