Как быстро заполнить пустой массив значениями из отдельных вызовов функции - PullRequest
8 голосов
/ 11 апреля 2019

Я хочу заполнить массив сгенерированными значениями.Эти значения генерируются функцией генератора.Длина массива не слишком велика, обычно <100, но этот массив генерируется много раз, поэтому я хотел знать, можно ли его оптимизировать при некотором причудливом использовании numpy. </p>

Пока я уже могу это сделатьс vanilla python:

def generate():
   return generated_data

array = np.asarray([generate() for _ in range(array_length)])

Я также пытался использовать np.full(shape, fill_value):

np.full((array_length, generated_data_size), generate())

Но это вызывает функцию generate() только один раз, а не один раз для каждого индексав массиве.

Я также пытался np.vectorize(), но не смог заставить его сгенерировать массив соответствующей формы.

Ответы [ 3 ]

2 голосов
/ 12 апреля 2019

Другой вариант - сделать ufunc из вашей функции generate:

gen_array = np.frompyfunc(generate, 0, 1) # takes 0 args, returns 1
array = gen_array(np.empty(array_length))

Это немного быстрее для меня, чем версия "Need for speed" из Sigve's answer .

1 голос
/ 12 апреля 2019

NumPy ничего не может сделать, чтобы ускорить процесс повторного вызова функции, не предназначенной для взаимодействия с NumPy.

"Необычный способ использования numpy" - способ оптимизировать это - вручную переписать вашу функцию generate, чтобы использовать операции NumPy для генерации целых массивов вывода вместо поддержки только отдельных значений. Вот как работает NumPy и как NumPy должен работать ; любое решение, которое включает в себя повторный вызов функции Python для каждой ячейки массива, будет ограничено накладными расходами Python. NumPy может только ускорить работу, которая фактически происходит в NumPy.

Если предоставляемые операции NumPy слишком ограничены для их перезаписи generate, существуют такие опции, как перезапись generate с помощью Cython или использование @numba.jit для него. Они в основном помогают в вычислениях, которые включают сложные зависимости от одной итерации цикла к другой; они не помогают с внешними зависимостями, которые вы не можете переписать.

Если вы не можете переписать generate, все, что вы можете сделать, это попытаться оптимизировать процесс получения возвращаемых значений в ваш массив. В зависимости от размера массива вы можете сэкономить время, повторно используя один объект массива:

In [32]: %timeit x = numpy.array([random.random() for _ in range(10)])
The slowest run took 5.13 times longer than the fastest. This could mean that an
 intermediate result is being cached.
100000 loops, best of 5: 5.44 µs per loop
In [33]: %%timeit x = numpy.empty(10)
   ....: for i in range(10):
   ....:     x[i] = random.random()
   ....: 
The slowest run took 4.26 times longer than the fastest. This could mean that an
 intermediate result is being cached.
100000 loops, best of 5: 2.88 µs per loop

но преимущество для больших массивов исчезает:

In [34]: %timeit x = numpy.array([random.random() for _ in range(100)])
10000 loops, best of 5: 21.9 µs per loop
In [35]: %%timeit x = numpy.empty(100)
   ....: for i in range(100):
   ....:     x[i] = random.random()
   ....: 
10000 loops, best of 5: 22.8 µs per loop
1 голос
/ 11 апреля 2019

Обычный "Pythoninc"

Понимание списка или функция карты могут быть для вас возможными решениями:

from random import random
import numpy as np

np.array(list(map(lambda idx: random(), range(10))))
np.array([random() for idx in range(10)])

"Потребность в скорости "

Может быть, предварительное выделение памяти уменьшит микросекунду или две (?)

array = np.empty(10)
for idx in range(10):
    array[idx] = random()

См. Ответ Натана для четноголучшее решение.

Векторизация функции

Функцию можно «векторизовать», используя numpy:

def rnd(x):
    return random()

fun = np.vectorize(rnd)
array = fun(range(10))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...