Как ускорить перечисление для массива Numpy / Как эффективно перечислить массив NumPy? - PullRequest
3 голосов
/ 20 марта 2012

Мне нужно сгенерировать много случайных чисел. Я пытался использовать random.random, но эта функция довольно медленная. Поэтому я переключился на numpy.random.random, что намного быстрее! Все идет нормально. Сгенерированные случайные числа фактически используются для вычисления некоторой вещи (на основе числа). Поэтому я enumerate над каждым числом и заменить значение. Это, кажется, убивает все мое ранее полученное ускорение. Вот статистика, сгенерированная с помощью timeit():

test_random - no enumerate
0.133111953735
test_np_random - no enumerate
0.0177130699158


test_random - enumerate
0.269361019135
test_np_random - enumerate
1.22525310516

Как видите, генерация числа почти в 10 раз быстрее с использованием numpy, но при перечислении по этим числам у меня равное время выполнения.

Ниже приведен код, который я использую:

import numpy as np
import timeit
import random

NBR_TIMES = 10
NBR_ELEMENTS = 100000

def test_random(do_enumerate=False):
    y = [random.random() for i in range(NBR_ELEMENTS)]
    if do_enumerate:
        for index, item in enumerate(y):
            # overwrite the y value, in reality this will be some function of 'item'
            y[index] = 1 + item

def test_np_random(do_enumerate=False):
    y = np.random.random(NBR_ELEMENTS)
    if do_enumerate:
        for index, item in enumerate(y):
            # overwrite the y value, in reality this will be some function of 'item'
            y[index] = 1 + item

if __name__ == '__main__':
    from timeit import Timer

    t = Timer("test_random()", "from __main__ import test_random")
    print "test_random - no enumerate"
    print t.timeit(NBR_TIMES)

    t = Timer("test_np_random()", "from __main__ import test_np_random")
    print "test_np_random - no enumerate"
    print t.timeit(NBR_TIMES)


    t = Timer("test_random(True)", "from __main__ import test_random")
    print "test_random - enumerate"
    print t.timeit(NBR_TIMES)

    t = Timer("test_np_random(True)", "from __main__ import test_np_random")
    print "test_np_random - enumerate"
    print t.timeit(NBR_TIMES)

Какой лучший способ ускорить это и почему enumerate так резко тормозит?

РЕДАКТИРОВАТЬ: причина, по которой я использую enumerate, заключается в том, что мне нужны и индекс, и значение текущего элемента.

Ответы [ 2 ]

6 голосов
/ 20 марта 2012

Чтобы в полной мере использовать скорость numpy, вы хотите создать ufuncs , когда это возможно. Применение vectorize к функции, как предлагает mgibsonbr , является одним из способов сделать это, но лучшим способом, если это возможно, является простое создание функции, которая использует преимущества встроенных функций numpy. Вот как то так:

>>> import numpy
>>> a = numpy.random.random(10)
>>> a + 1
array([ 1.29738145,  1.33004628,  1.45825441,  1.46171177,  1.56863326,
        1.58502855,  1.06693054,  1.93304272,  1.66056379,  1.91418473])
>>> (a + 1) * 0.25 / 4
array([ 0.08108634,  0.08312789,  0.0911409 ,  0.09135699,  0.09803958,
        0.09906428,  0.06668316,  0.12081517,  0.10378524,  0.11963655])

Какова природа функции, которую вы хотите применить к массиву numpy? Если вы сообщите нам, возможно, мы сможем помочь вам создать версию, которая использует только numpy ufuncs.

Также возможно генерировать массив индексов без использования enumerate. Numpy предоставляет ndenumerate, который является итератором и, вероятно, медленнее, но он также предоставляет indices, который является очень быстрым способом генерирования индексов, соответствующих значениям в массив. Итак ...

>>> numpy.indices(a.shape)
array([[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]])

Чтобы быть более точным, вы можете использовать вышеперечисленное и комбинировать их, используя numpy.rec.fromarrays:

>>> a = numpy.random.random(10)
>>> ind = numpy.indices(a.shape)
>>> numpy.rec.fromarrays([ind[0], a])
rec.array([(0, 0.092473494150913438), (1, 0.20853257641948986),
       (2, 0.35141455604686067), (3, 0.12212258656960817),
       (4, 0.50986868372639049), (5, 0.0011439325711705139),
       (6, 0.50412473457942508), (7, 0.28973489788728601),
       (8, 0.20078799423168536), (9, 0.34527678271856999)], 
      dtype=[('f0', '<i8'), ('f1', '<f8')])

Звучит так, будто ваша главная задача - выполнить операцию на месте. Это сложнее сделать с помощью vectorize, но это легко сделать с помощью подхода ufunc:

>>> def somefunc(a):
...     a += 1
...     a /= 15
... 
>>> a = numpy.random.random(10)
>>> b = a
>>> somefunc(a)
>>> a
array([ 0.07158446,  0.07052393,  0.07276768,  0.09813235,  0.09429439,
        0.08561703,  0.11204622,  0.10773558,  0.11878885,  0.10969279])
>>> b
array([ 0.07158446,  0.07052393,  0.07276768,  0.09813235,  0.09429439,
        0.08561703,  0.11204622,  0.10773558,  0.11878885,  0.10969279])

Как видите, numpy выполняет эти операции на месте.

3 голосов
/ 20 марта 2012

Проверьте numpy.vectorize , это должно позволить вам применять произвольные функции к массивам numpy. Для вашего простого примера вы бы сделали что-то вроде этого:

vecFunc = vectorize(lambda x: x + 1)
vecFunc(y)

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

В общем, вы всегда будете лучше манипулировать numpy-структурами с numpy-функциями, чем итерировать с помощью функций python, поскольку первые не только оптимизированы, но и реализованы в C, а последние всегда будут интерпретироваться.

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