python & numpy: сумма среза массива - PullRequest
9 голосов
/ 05 мая 2011

У меня есть одномерный массив NumPy (array_) и список Python (список _).

Следующий код работает, но неэффективен, потому что срезы включают ненужную копию (конечно, для списков Python, и я полагаю, также и для пустых массивов?):

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

Какой хороший способ переписать это?

Ответы [ 4 ]

13 голосов
/ 05 мая 2011

Нарезка пустого массива не делает копию, как в случае со списком.

В качестве основного примера:

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() вместо вызова встроенного в 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))

В большинстве случаев это излишне.Если вы работаете со списками достаточно долго, чтобы управление памятью являлось серьезной проблемой, то вам, вероятно, не следует использовать список для хранения ваших значений.(Списки не предназначены или не предназначены для компактного хранения элементов в памяти.)

Кроме того, вы не захотите делать это для пустых массивов.Простое выполнение some_array[1:].sum() будет на несколько порядков быстрее и не будет использовать любой больше памяти, чем islice.

8 голосов
/ 05 мая 2011

Мой первый инстинкт был таким же, как у Джо Кингтона, когда дело доходит до списков, но я проверил, и по крайней мере на моей машине 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

Я не мог придумать, что еще проверить. (Мысли, кто-нибудь?)

3 голосов
/ 05 мая 2011

@ Джо Кингтон (это временный ответ, чтобы просто показать мои тайминги, я скоро его уберу):

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

Насколько мне известно из моего источника (1.5.1), sum(.)это просто обёртка для x.sum(.).Таким образом, при больших входах время выполнения одинаково (асимптотически) для sum(.) и x.sum(.).

Редактировать : Этот ответ должен был быть только временным, но на самом деле он (и его комментарии) действительно может быть кому-то полезен.Поэтому я просто оставлю все как есть, пока кто-нибудь действительно не попросит меня удалить его.

0 голосов
/ 05 мая 2011

Я не нахожу x[1:].sum() значительно медленнее, чем x.sum(). Для списков sum(x) - x[0] быстрее sum(x[1:]) (примерно на 40% быстрее OMM).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...