2016-11-14 4 views
5

Я заметил значительную разницу между итерацией через массив numpy «прямо» и повторением с помощью метода tolist. См времени ниже:Каков самый быстрый способ итерации через массив numpy

непосредственно
[i for i in np.arange(10000000)]
с помощью tolist
[i for i in np.arange(10000000).tolist()]

enter image description here


Учитывая, что я обнаружил один способ ускорить работу. Я хотел спросить, что еще может ускорить его?

Что такое быстрый способ итерации через массив numpy?

+0

это * - * нечетный. Я пробовал это сам несколько раз, и кажется, что преобразование его в список делает его быстрее все время. Спасибо, что принесли это на свет. –

+1

Просто повторите попытку и получите список или выполните некоторую обработку тоже? Использование только 'list (np.arange (1000000))' выглядит довольно быстро. – Divakar

+0

@Divakar см. Http://stackoverflow.com/a/40575522/2336654 – piRSquared

ответ

4

Это мои тайминги на медленной машине

In [1034]: timeit [i for i in np.arange(10000000)] 
1 loop, best of 3: 2.16 s per loop 

Если я произвожу диапазон непосредственно (PY3 так это Genertor) раз намного лучше. Возьмите это базовое значение для понимания списка такого размера.

In [1035]: timeit [i for i in range(10000000)] 
1 loop, best of 3: 1.26 s per loop 

tolist преобразует arange в список первым; занимает немного больше времени, но итерация по-прежнему в списке

In [1036]: timeit [i for i in np.arange(10000000).tolist()] 
1 loop, best of 3: 1.6 s per loop 

Использование list() - то же самое время, как прямой итерации на массиве; что предполагает, что первая итерация сначала делает это.

In [1037]: timeit [i for i in list(np.arange(10000000))] 
1 loop, best of 3: 2.18 s per loop 

In [1038]: timeit np.arange(10000000).tolist() 
1 loop, best of 3: 927 ms per loop 

же раз в итерация на .tolist

In [1039]: timeit list(np.arange(10000000)) 
1 loop, best of 3: 1.55 s per loop 

В общем, если вы должны цикл, работая в списке быстрее. Доступ к элементам списка проще.

Посмотрите на элементы, возвращаемые индексированием.

a[0] - еще один numpy объект; он строится из значений в a, но не просто извлекаемое значение

list(a)[0] - это тот же тип; список только [a[0], a[1], a[2]]]

In [1043]: a = np.arange(3) 
In [1044]: type(a[0]) 
Out[1044]: numpy.int32 
In [1045]: ll=list(a) 
In [1046]: type(ll[0]) 
Out[1046]: numpy.int32 

но tolist преобразует массив в чистый список, в этом случае, как список Интс. Он работает больше, чем list(), но делает это в скомпилированном коде.

In [1047]: ll=a.tolist() 
In [1048]: type(ll[0]) 
Out[1048]: int 

В общем не используйте list(anarray). Он редко делает что-то полезное и не так сильно, как tolist().

Какой самый быстрый способ перебора массива - Нет. По крайней мере, не в Python; в c-коде есть быстрые пути.

a.tolist() - это самый быстрый и векторный способ создания целых чисел из массива. Он выполняет итерацию, но делает это в скомпилированном коде.

Но какова ваша реальная цель?

+0

Благодаря @hpaulj это очень близко к тому, чтобы ответить на мой вопрос в том, что вы заявили ... «Какой самый быстрый способ перебора массива - Нет». Я, скорее всего, выберу это как свой ответ, но я оставляю его открытым. – piRSquared

7

Это на самом деле не удивительно. Давайте рассмотрим методы, которые начинаются с самого медленного.

[i for i in np.arange(10000000)] 

Этот метод требует Python, чтобы достигнуть в Numpy массива (хранится в объеме памяти C), одного элемента в то время, выделить объект Python в памяти, и создать указатель на этот объект в списке. Каждый раз, когда вы прокладываете между массивом numpy, хранящимся в C-сервере, и вытаскиваете его в чистый питон, есть накладные расходы. Этот метод добавляет в эту стоимость 10 000 000 раз.

Следующая:

[i for i in np.arange(10000000).tolist()] 

В этом случае, используя .tolist() делает один вызов к Numpy C бэкэндом и выделяет все элементы в одном кадре в списке. Затем вы используете python для итерации по этому списку.

Наконец:

list(np.arange(10000000)) 

Это в основном делает то же самое, что и выше, но это создает список объектов нативных типа Numpy в (например, np.int64). Использование list(np.arange(10000000)) и np.arange(10000000).tolist() должно быть примерно в то же время.


Таким образом, с точки зрения итерации, основным преимуществом использования numpy является то, что вам не нужно перебирать. Операция применяется в векторном виде по массиву. Итерация просто замедляет ее. Если вы обнаружите, что выполняете итерации по элементам массива, вы должны найти способ реструктурировать алгоритм, который вы пытаетесь, таким образом, чтобы использовать только операции с numpy (у него есть много встроенных!), Или если вам действительно нужно, вы можете используйте np.apply_along_axis, np.apply_over_axis, или np.vectorize.

+2

Но есть тонкая разница между 'list (np.arange (10))' и 'np.arange (10) .tolist()': первый приведет к списку 'np.int64' второго в список python 'int's. Первый может быть проблематичным для выполнения таких операций, как сериализация, например. используя json. json будет ошибкой на первом, потому что он не может обрабатывать 'np.int64' – MaxNoe

+0

Вы абсолютно правы. – James

+0

Это очень полезно, и поэтому я поддержал его, и я надеюсь, что другие делают это. На данный момент я оставляю вопрос открытым, так как мне все еще остается желать увидеть другие варианты итерации через массив. – piRSquared