Каков наиболее эффективный способ отобразить функцию на массиве? В моем текущем проекте я делал это следующим образом:
import numpy as np
x = np.array([1, 2, 3, 4, 5])
# Obtain array of square of each element in x
squarer = lambda t: t ** 2
squares = np.array([squarer(xi) for xi in x])
Тем не менее, кажется, что это, вероятно, очень неэффективно, поскольку я использую понимание списка для создания нового массива в виде списка Python, прежде чем преобразовывать его обратно в пустой массив.
Можем ли мы сделать лучше?
python
performance
numpy
Райан
источник
источник
squarer(x)
?x = np.array([1, 2, 3, 4, 5]); x**2
работыОтветы:
Я проверил все предложенные методы плюс
np.array(map(f, x))
сperfplot
(мой небольшой проект).Если функция , которую вы пытаетесь уже векторизациями в векторной (как ,
x**2
например , в исходном сообщении), используя это гораздо быстрее , чем все остальное (обратите внимание на логарифмическую шкалу):Если вам действительно нужна векторизация, не имеет большого значения, какой вариант вы используете.
Код для воспроизведения сюжетов:
источник
f(x)
из вашего заговора. Это может быть неприменимо для каждогоf
, но оно применимо здесь, и это легко самое быстрое решение, когда применимо.vf = np.vectorize(f); y = vf(x)
выигрывает для коротких входов.pip install -U perfplot
) я вижу сообщение:AttributeError: 'module' object has no attribute 'save'
при вставке примера кода.Как насчет использования
numpy.vectorize
.источник
The vectorize function is provided primarily for convenience, not for performance. The implementation is essentially a for loop.
В других вопросах я обнаружил, что этоvectorize
может удвоить скорость итерации пользователя. Но реальное ускорение происходит с реальнымиnumpy
операциями с массивами.squarer(x)
уже работал бы для не 1-мерных массивов.vectorize
только действительно имеет какое-либо преимущество перед пониманием списка (как тот, что в вопросе), а не надsquarer(x)
.TL; DR
Как отмечено @ user2357112 , «прямой» метод применения функции - это всегда самый быстрый и простой способ отобразить функцию на массивах Numpy:
Вообще избегайте
np.vectorize
, так как он неэффективен и имеет (или имел) ряд проблем . Если вы работаете с другими типами данных, вы можете изучить другие методы, показанные ниже.Сравнение методов
Вот несколько простых тестов для сравнения трех методов для сопоставления функции, этот пример используется с Python 3.6 и NumPy 1.15.4. Во-первых, настройки функций для тестирования:
Тестирование с пятью элементами (отсортировано от самого быстрого до самого медленного):
С сотнями элементов:
И с тысячами элементов массива или более:
Разные версии Python / NumPy и оптимизация компилятора будут иметь разные результаты, поэтому проведите аналогичный тест для вашей среды.
источник
count
аргумент и выражение генератора, тоnp.fromiter
это значительно быстрее.'np.fromiter((f(xi) for xi in x), x.dtype, count=len(x))'
f(x)
, которое на порядок превосходит все остальное .f
имеет 2 переменные и массив 2D?Вокруг есть числаxpr , numba и cython , цель этого ответа - принять во внимание эти возможности.
Но сначала давайте констатируем очевидное: независимо от того, как вы отображаете Python-функцию на массив numpy, она остается функцией Python, что означает для каждой оценки:
Float
).То, какой механизм используется для циклического прохождения массива, не играет большой роли из-за упомянутых выше издержек - он работает намного медленнее, чем использование встроенной функциональности numpy.
Давайте посмотрим на следующий пример:
np.vectorize
выбран в качестве представителя класса подходов чисто Python функции. Используяperfplot
(см. Код в приложении к этому ответу), мы получаем следующее время выполнения:Мы можем видеть, что numpy-подход в 10-100 раз быстрее, чем в чистой версии Python. Вероятно, снижение производительности при больших размерах массивов связано с тем, что данные больше не помещаются в кэш.
Стоит также упомянуть, что он
vectorize
также использует много памяти, поэтому часто использование памяти является узким местом (см. Соответствующий вопрос SO ). Также обратите внимание, что в документации numpynp.vectorize
говорится, что она «предоставляется в основном для удобства, а не для производительности».При желании использовать другие инструменты, кроме написания C-расширения с нуля, существуют следующие возможности:
Часто можно услышать, что производительность NumPy настолько хороша, насколько это возможно, потому что это чистый C под капотом. Тем не менее, есть много возможностей для совершенствования!
Векторизованная numpy-версия использует много дополнительной памяти и обращений к памяти. Numexp-library пытается упорядочить numpy-массивы и таким образом получить лучшее использование кэша:
Приводит к следующему сравнению:
Я не могу объяснить все на графике выше: вначале мы видим большие издержки для библиотеки numbersxpr, но поскольку она лучше использует кэш, она примерно в 10 раз быстрее для больших массивов!
Другой подход состоит в том, чтобы выполнить jit-компиляцию функции и, таким образом, получить настоящий UFunc на чистом C. Это подход Нумбы:
Это в 10 раз быстрее, чем оригинальный numpy-подход:
Однако задача смущающе распараллеливается, поэтому мы также можем использовать ее
prange
для параллельного вычисления цикла:Как и ожидалось, параллельная функция медленнее для небольших входов, но быстрее (почти в 2 раза) для больших размеров:
В то время как numba специализируется на оптимизации операций с numpy-массивами, Cython является более общим инструментом. Извлечь ту же производительность, что и с numba, сложнее - часто она снижается до llvm (numba) по сравнению с локальным компилятором (gcc / MSVC):
Cython приводит к несколько более медленным функциям:
Вывод
Очевидно, что тестирование только для одной функции ничего не доказывает. Также следует помнить, что для выбранной функции-примера пропускная способность памяти была узким местом для размеров, превышающих 10 ^ 5 элементов - таким образом, мы имели одинаковую производительность для numba, figurexpr и cython в этой области.
В конце концов, окончательный ответ зависит от типа функции, аппаратного обеспечения, Python-распределения и других факторов. Например , Анаконда-распределение использует Intel, VML для функций Numpy и , таким образом , превосходит по Numba (если он не использует SVML, увидеть этот SO-пост ) легко для трансцендентных функций , такие как
exp
,sin
,cos
и аналогичного - смотрите , например , следующий SO-пост .Тем не менее, исходя из этого исследования и моего опыта, я до сих пор утверждаю, что нумба кажется самым простым инструментом с наилучшими характеристиками, если не задействованы трансцендентные функции.
График времени прохождения с перфлот-пакетом :
источник
Арифметические операции над массивами автоматически применяются поэлементно, с эффективными циклами уровня C, позволяющими избежать всех накладных расходов интерпретатора, которые могут применяться к циклу или пониманию уровня Python.
Большинство функций, которые вы хотите применить к массиву NumPy, будут просто работать, хотя некоторые могут нуждаться в изменениях. Например,
if
не работает поэлементно. Вы хотели бы преобразовать те, чтобы использовать конструкции какnumpy.where
:становится
источник
Во многих случаях numpy.apply_along_axis будет лучшим выбором. Это увеличивает производительность примерно в 100 раз по сравнению с другими подходами - и не только для тривиальных функций тестирования, но также и для более сложных композиций функций из numpy и scipy.
Когда я добавляю метод:
к коду perfplot я получаю следующие результаты:
источник
Я верю, что в более новой версии (я использую 1.13) numpy вы можете просто вызвать функцию, передав массив numpy функции, которую вы написали для скалярного типа, она автоматически применит вызов функции к каждому элементу массива numpy и вернет вам другой массив NumPy
источник
**
оператор , который применяя вычисление для каждого элемента тt
. Это обычный NumPy. Заворачивание вlambda
ничего лишнего не делает.Кажется, никто не упомянул о встроенном заводском методе производства
ufunc
в упаковках с кусочками,np.frompyfunc
который я снова проверилnp.vectorize
и превзошел его примерно на 20-30%. Конечно, он будет работать хорошо, как предписано кодом C или дажеnumba
(который я не проверял), но это может быть лучшей альтернативой, чемnp.vectorize
Я также проверил большие образцы, и улучшение пропорционально. Смотрите документацию также здесь
источник
Как уже упоминалось в этом посте , просто используйте выражения генератора следующим образом:
источник
Все приведенные выше ответы хорошо сравниваются, но если вам нужно использовать пользовательскую функцию для отображения, и у вас есть
numpy.ndarray
, и вам нужно сохранить форму массива.У меня есть только два сравнения, но оно сохранит форму
ndarray
. Я использовал массив с 1 млн. Записей для сравнения. Здесь я использую квадратную функцию, которая также встроена в numpy и имеет большой прирост производительности, поскольку там, где это было необходимо, вы можете использовать функцию по вашему выбору.Вывод
Здесь вы можете ясно видеть, что
numpy.fromiter
работает замечательно, учитывая простой подход, и если доступна встроенная функция, пожалуйста, используйте это.источник
использование
numpy.fromfunction(function, shape, **kwargs)
См. « Https://docs.scipy.org/doc/numpy/reference/generated/numpy.fromfunction.html ».
источник