numpy.amax () найдет максимальное значение в массиве, а numpy.amin () сделает то же самое для минимального значения. Если я хочу найти как max, так и min, мне нужно вызвать обе функции, что требует дважды передать (очень большой) массив, что кажется медленным.
Есть ли в numpy API функция, которая находит как max, так и min только за один проход через данные?
amax
amin
minmax
в рассматриваемую библиотеку ( github.com/numpy/numpy/issues/9836 ).Ответы:
Нет. На момент написания такой функции не было. (И да, если там были такие функции, его производительность будет значительно лучше , чем вызов
numpy.amin()
иnumpy.amax()
последовательно на большом массиве.)источник
Не думаю, что двойное прохождение массива - проблема.Рассмотрим следующий псевдокод:Хотя здесь всего 1 цикл, есть еще 2 проверки. (Вместо 2 петель по 1 проверке в каждой). На самом деле единственное, что вы экономите, - это накладные расходы на 1 цикл. Если массивы действительно велики, как вы говорите, эти накладные расходы малы по сравнению с реальной рабочей нагрузкой цикла. (Обратите внимание, что все это реализовано в C, поэтому циклы в любом случае более или менее свободны).
РЕДАКТИРОВАТЬ Извините четверых из вас, кто поддержал меня и поверил в меня. Вы определенно можете это оптимизировать.
Вот код fortran, который можно скомпилировать в модуль Python с помощью
f2py
(возможно,Cython
гуру может прийти и сравнить его с оптимизированной версией C ...):Скомпилируйте его через:
И теперь мы находимся в месте, где можем это проверить:
Результаты для меня немного ошеломляют:
Должен сказать, я этого не совсем понимаю. Сравнение просто
np.min
противminmax1
иminmax2
все еще проигрышная битва, так что это не просто проблема памяти ...примечания - Увеличение размера в раз
10**a
и уменьшение повторения в раз10**a
(сохранение размера проблемы постоянным) действительно изменяет производительность, но не кажущимся последовательным образом, что показывает, что существует некоторая взаимосвязь между производительностью памяти и накладными расходами на вызов функций в питон. Даже сравнение простойmin
реализации в fortran превосходит numpy примерно в 2 раза ...источник
i < minval
истинно, тоi > maxval
всегда ложно, поэтому вам нужно в среднем делать 1,5 проверки на итерацию, когда втораяif
заменяется наelif
.f2py
просто обертывает кодированный вручную Fortran, чтобы его можно было вызывать из Python. «Более справедливым» тестом, вероятно, является ручное кодирование C, а затем использованиеf2py
(!) Его для Python. Если вы разрешаете C ++, то Shed Skin может быть идеальным местом для балансировки простоты программирования и производительности.Есть функция для поиска (max-min) под названием numpy.ptp, если это вам полезно:
но я не думаю, что есть способ найти как минимум, так и максимум за один обход.
EDIT: ptp просто вызывает min и max под капотом
источник
Вы можете использовать Numba , динамический компилятор Python с поддержкой NumPy, использующий LLVM. Полученная реализация довольно проста и понятна:
Он также должен быть быстрее, чем у Numpy
min() & max()
. И все это без необходимости писать ни одной строчки кода на C / Fortran.Проведите собственные тесты производительности, так как это всегда зависит от вашей архитектуры, ваших данных, версий вашего пакета ...
источник
numba
функцию один раз перед тестом, чтобы убедиться, что она скомпилирована JIT ? Кроме того, если вы используетеipython
, для простоты, я бы посоветовал вам использовать%timeit whatever_code()
для измерения времени выполнения.elif
позволяет вашему минимуму быть больше, чем вашему максимуму. Например, для массива длиной 1 максимальное значение будет любым, а min равно + бесконечности. Ничего страшного для одноразового, но не хорошего кода, чтобы бросить его глубоко в чрево производственного зверя.В общем, вы можете уменьшить количество сравнений для алгоритма minmax, обрабатывая два элемента за раз и сравнивая только меньший с временным минимумом и больший с временным максимумом. В среднем нужно всего 3/4 сравнений, чем наивный подход.
Это может быть реализовано на c или fortran (или на любом другом низкоуровневом языке) и должно быть практически непревзойденным с точки зрения производительности. я используюNumba чтобы проиллюстрировать принцип и получить очень быструю реализацию, не зависящую от dtype:
Это определенно быстрее, чем наивный подход, представленный Пеке :
Как и ожидалось, новая реализация minmax занимает примерно 3/4 времени, которое потребовалось наивной реализации (
2.1 / 2.75 = 0.7636363636363637
)источник
Просто чтобы получить представление о числах, которых можно было ожидать, учитывая следующие подходы:
(
extrema_loop_*()
подходы аналогичны предлагаемым здесь , аextrema_while_*()
подходы основаны на коде отсюда )Следующие сроки:
указывают, что
extrema_while_*()
они самые быстрые,extrema_while_nb()
причем самые быстрые. В любом случае, такжеextrema_loop_nb()
иextrema_loop_cy()
растворы делают опережать NumPy-только подход ( с использованиемnp.max()
иnp.min()
отдельно).Наконец, обратите внимание, что ни один из них не является таким гибким, как
np.min()
/np.max()
(с точки зрения поддержки n-dim,axis
параметра и т. Д.).(полный код доступен здесь )
источник
extrema_while_nb
Никто не упомянул numpy.percentile , поэтому я подумал, что буду. Если вы спросите
[0, 100]
процентили, он даст вам массив из двух элементов: минимального (0-го процентиля) и максимального (100-го процентиля).Однако это не удовлетворяет цели OP: он не быстрее min и max по отдельности. Это, вероятно , из - за какой - то механизм , который позволил бы без экстремальных процентили (более жесткой проблемой, которая должна занять больше времени).
В будущей версии Numpy можно было бы предусмотреть специальный случай, чтобы пропустить обычный расчет процентилей, если
[0, 100]
это необходимо. Не добавляя ничего к интерфейсу, есть способ запросить Numpy для min и max за один вызов (вопреки тому, что было сказано в принятом ответе), но стандартная реализация библиотеки не использует этот случай, чтобы сделать это стоит.источник
Это старая ветка, но в любом случае, если кто-нибудь когда-нибудь посмотрит на это снова ...
При одновременном поиске минимального и максимального значений можно уменьшить количество сравнений. Если вы сравниваете числа с плавающей запятой (что, я думаю, так и есть), это может сэкономить вам время, хотя и не вычислительной сложности.
Вместо (код Python):
вы можете сначала сравнить два соседних значения в массиве, а затем сравнить только меньшее с текущим минимумом, а большее с текущим максимумом:
Код здесь написан на Python, явно для скорости вы должны использовать C, Fortran или Cython, но таким образом вы выполняете 3 сравнения на итерацию с итерациями len (ar) / 2, что дает 3/2 * len (ar) сравнения. В отличие от этого, выполняя сравнение «очевидным способом», вы выполняете два сравнения за итерацию, что приводит к 2 * len (ar) сравнения. Экономит 25% времени на сравнение.
Может быть, кто-нибудь однажды сочтет это полезным.
источник
np.bincount
, см. Здесь . Он не использует указанный вами трюк, потому что он оказался в 2 раза медленнее, чем наивный подход. В PR есть ссылка на некоторые исчерпывающие тесты обоих методов.На первый взгляд кажется, что все получается:
numpy.histogram
... но если вы посмотрите на источник этой функции, он просто вызывает
a.min()
иa.max()
независимо и, следовательно, не может избежать проблем с производительностью, рассматриваемых в этом вопросе. :-(Точно
scipy.ndimage.measurements.extrema
похоже на возможность, но тоже просто звонитa.min()
иa.max()
самостоятельно.источник
np.histogram
не всегда работает для этого, поскольку возвращаемые(amin, amax)
значения относятся к минимальному и максимальному значениям корзины. Если у меня, напримерa = np.zeros(10)
,np.histogram(a, bins=1)
возвращается(array([10]), array([-0.5, 0.5]))
. В этом случае пользователь ищет(amin, amax)
= (0, 0).В любом случае, для меня это стоило усилий, поэтому я предлагаю здесь самое сложное и наименее элегантное решение для всех, кто может быть заинтересован. Мое решение - реализовать многопоточный алгоритм min-max за один проход на C ++ и использовать его для создания модуля расширения Python. Это требует дополнительных затрат на изучение того, как использовать API-интерфейсы Python и NumPy C / C ++, и здесь я покажу код и дам небольшие пояснения и ссылки для тех, кто хочет пойти по этому пути.
Многопоточный мин. / Макс.
Здесь нет ничего особо интересного. Массив разбивается на куски по размеру
length / workers
. Минимум / максимум рассчитывается для каждого фрагмента в afuture
, который затем сканируется на предмет глобального минимума / максимума.Модуль расширения Python
Здесь все начинает становиться некрасивым ... Один из способов использования кода C ++ в Python - реализовать модуль расширения. Этот модуль можно собрать и установить с помощью
distutils.core
стандартного модуля. Полное описание того, что это влечет за собой, содержится в документации Python: https://docs.python.org/3/exnding/exnding.html . ПРИМЕЧАНИЕ: безусловно, есть и другие способы получить аналогичные результаты, цитируя https://docs.python.org/3/exnding/index.html#exnding-index :По сути, этот путь скорее академический, чем практический. С учетом вышесказанного, что я сделал дальше, довольно близко придерживаясь этого руководства, создал файл модуля. По сути, это шаблон для distutils, который знает, что делать с вашим кодом, и создает из него модуль Python. Прежде чем делать что-либо из этого, вероятно, будет разумным создать виртуальную среду Python, чтобы не загрязнять системные пакеты (см. Https://docs.python.org/3/library/venv.html#module-venv ).
Вот файл модуля:
В этом файле широко используется Python, а также API NumPy, для получения дополнительной информации обратитесь: https://docs.python.org/3/c-api/arg.html#c.PyArg_ParseTuple и для NumPy : https://docs.scipy.org/doc/numpy/reference/c-api.array.html .
Установка модуля
Следующее, что нужно сделать, это использовать distutils для установки модуля. Для этого требуется установочный файл:
Чтобы окончательно установить модуль, выполните его
python3 setup.py install
из виртуальной среды.Тестирование модуля
Наконец, мы можем проверить, действительно ли реализация C ++ превосходит простое использование NumPy. Для этого вот простой тестовый скрипт:
Вот результаты, которые я получил от всего этого:
Это гораздо менее обнадеживающе, чем результаты, показанные ранее в потоке, которые указывают примерно на 3,5-кратное ускорение и не включают многопоточность. Достигнутые мной результаты в некоторой степени разумны, я ожидал, что накладные расходы на потоки и будут преобладать во время, пока массивы не станут очень большими, и в этот момент увеличение производительности начнет приближаться к
std::thread::hardware_concurrency
увеличению x.Вывод
Казалось бы, есть место для оптимизации некоторого кода NumPy для конкретных приложений, в частности, в отношении многопоточности. Мне не ясно, стоит ли это усилий, но это определенно кажется хорошим упражнением (или чем-то еще). Я думаю, что, возможно, изучение некоторых из этих «сторонних инструментов», таких как Cython, может быть более эффективным использованием времени, но кто знает.
источник
v = min_max_it->get();
. Вget
методе блокируется , пока результат не будет готов и возвращает его. Поскольку цикл проходит через каждое будущее, он не завершится, пока все они не будут выполнены. future.get ()Самый короткий способ, который я придумал, таков:
Но поскольку он сортирует массив, он не самый эффективный.
Другой короткий способ:
Это должно быть более эффективным, но результат вычисляется и возвращается число с плавающей запятой.
источник