numpy: функция для одновременного max () и min()
numpy.amax () найти максимальное значение в массиве, и numpy.Амин() делает то же самое минимальное значение. Если я хочу найти как max, так и min, мне нужно вызвать обе функции, что требует прохождения через (очень большой) массив дважды, что кажется медленным.
есть ли функция в API numpy, которая находит как max, так и min только с одним проходом через данные?
8 ответов:
есть ли функция в API numpy, которая находит как max, так и min только с одним проходом через данные?
нет. На момент написания этой статьи такой функции не было. (И да, если там были такая функция, ее производительность будет значительно лучше, чем звонить
numpy.amin()иnumpy.amax()последовательно на большом массиве.)
я не думаю, что передача массива дважды является проблемой.рассмотрим следующий псевдокод:minval = array[0] maxval = array[0] for i in array: if i < minval: minval = i if i > maxval: maxval = iв то время как есть только 1 цикл здесь, есть еще 2 проверки. (Вместо 2 петель с 1 проверить каждый). На самом деле единственное, что вы экономите, это накладные расходы на 1 цикл. Если массивы действительно большие, как вы говорите, что накладные расходы невелики по сравнению с фактической рабочей нагрузкой цикла. (Обратите внимание, что все это реализовано в C, поэтому циклы больше или в любом случае, менее бесплатно).
EDIT извините за 4 из вас, кто проголосовал и верил в меня. Вы определенно можете оптимизировать это.
вот некоторые fortran код, который может быть скомпилирован в модуль python через
f2py(например,Cythonгуру может прийти и сравнить это с оптимизированной версией C ...):subroutine minmax1(a,n,amin,amax) implicit none !f2py intent(hidden) :: n !f2py intent(out) :: amin,amax !f2py intent(in) :: a integer n real a(n),amin,amax integer i amin = a(1) amax = a(1) do i=2, n if(a(i) > amax)then amax = a(i) elseif(a(i) < amin) then amin = a(i) endif enddo end subroutine minmax1 subroutine minmax2(a,n,amin,amax) implicit none !f2py intent(hidden) :: n !f2py intent(out) :: amin,amax !f2py intent(in) :: a integer n real a(n),amin,amax amin = minval(a) amax = maxval(a) end subroutine minmax2скомпилировать его через:
f2py -m untitled -c fortran_code.f90и сейчас мы находимся в месте, где мы можем проверить это:
import timeit size = 100000 repeat = 10000 print timeit.timeit( 'np.min(a); np.max(a)', setup='import numpy as np; a = np.arange(%d, dtype=np.float32)' % size, number=repeat), " # numpy min/max" print timeit.timeit( 'untitled.minmax1(a)', setup='import numpy as np; import untitled; a = np.arange(%d, dtype=np.float32)' % size, number=repeat), '# minmax1' print timeit.timeit( 'untitled.minmax2(a)', setup='import numpy as np; import untitled; a = np.arange(%d, dtype=np.float32)' % size, number=repeat), '# minmax2'результаты немного ошеломляют меня:
8.61869883537 # numpy min/max 1.60417699814 # minmax1 2.30169081688 # minmax2Я должен сказать, я не совсем понимаю. Сравнение просто
np.minиminmax1иminmax2еще проигранная битва, так что это не просто вопрос памяти ...Примечания -- увеличение размера в несколько раз
10**aи уменьшение повторения в несколько раз10**a(сохраняя размер проблемы постоянным) действительно изменяет производительность, но не в кажущемся последовательный способ, который показывает, что существует некоторое взаимодействие между производительностью памяти и служебными данными вызова функций в python. Даже сравнивая простойminреализация на Фортране бьет и NumPy это примерно 2 ...
есть функция для поиска (max-min) называется numpy.ПТП если это полезно для вас:
>>> import numpy >>> x = numpy.array([1,2,3,4,5,6]) >>> x.ptp() 5но я не думаю, что есть способ найти как min, так и max с одним обходом.
вы могли бы использовать Numba, который является numpy-aware динамический компилятор Python с использованием LLVM. Полученная реализация довольно проста и понятна:
import numpy import numba @numba.jit def minmax(x): maximum = x[0] minimum = x[0] for i in x[1:]: if i > maximum: maximum = i elif i < minimum: minimum = i return (minimum, maximum) numpy.random.seed(1) x = numpy.random.rand(1000000) print(minmax(x) == (x.min(), x.max()))Он должен быть быстрее, чем NumPy и обратно в
min() & max()реализация. И все это без необходимости писать одну строку кода C/Fortran.сделайте свои собственные тесты производительности, так как это всегда зависит от вашей архитектуры, ваших данных, ваших версий пакетов...
Это старый поток, но в любом случае, если кто-нибудь когда-нибудь посмотрит на это снова...
при поиске мин и Макс одновременно, можно уменьшить количество сравнений. Если это поплавки, которые вы сравниваете (что, я думаю, так и есть), это может сэкономить вам некоторое время, хотя и не вычислительная сложность.
вместо (код Python):
_max = ar[0] _min= ar[0] for ii in xrange(len(ar)): if _max > ar[ii]: _max = ar[ii] if _min < ar[ii]: _min = ar[ii]вы можете сначала сравнить два соседних значения в массиве, а затем сравнить только меньшее против текущего минимума, а больший против текущего максимума:
## for an even-sized array _max = ar[0] _min = ar[0] for ii in xrange(0, len(ar), 2)): ## iterate over every other value in the array f1 = ar[ii] f2 = ar[ii+1] if (f1 < f2): if f1 < _min: _min = f1 if f2 > _max: _max = f2 else: if f2 < _min: _min = f2 if f1 > _max: _max = f1код здесь написан на Python, очевидно, для скорости вы бы использовали C или Fortran или Cython, но таким образом Вы делаете 3 сравнения на итерацию, с LEN(ar)/2 итерациями, давая 3/2 * LEN(ar) сравнения. В отличие от этого, делая сравнение "очевидным способом", вы делаете два сравнения на итерацию, что приводит к сравнениям 2*len(ar). Вы экономите 25% времени сравнения.
может, кто-то один день найдет это полезным.
на первый взгляд
numpy.histogramпоявляется сделать трюк:count, (amin, amax) = numpy.histogram(a, bins=1)... но если вы посмотрите на источник для этой функции, он просто называет
a.min()иa.max()независимо, и поэтому не удается избежать проблем с производительностью, рассмотренных в этом вопросе. : - (аналогично,
scipy.ndimage.measurements.extremaпохоже на возможность, но она тоже просто вызываетa.min()иa.max()самостоятельно.
В общем случае вы можете уменьшить количество сравнений для алгоритма minmax, обрабатывая два элемента одновременно и сравнивая только меньший с временным минимумом и больший с временным максимумом. В среднем требуется только 3/4 сравнений, чем наивный подход.
Это может быть реализовано на c или fortran (или любом другом языке низкого уровня) и должно быть почти непревзойденным с точки зрения производительности. Я использую numba для иллюстрации принцип и получить очень быстрый, dtype-независимая реализация:
import numba as nb import numpy as np @nb.njit def minmax(array): # Ravel the array and return early if it's empty array = array.ravel() length = array.size if not length: return # We want to process two elements at once so we need # an even sized array, but we preprocess the first and # start with the second element, so we want it "odd" odd = length % 2 if not odd: length -= 1 # Initialize min and max with the first item minimum = maximum = array[0] i = 1 while i < length: # Get the next two items and swap them if necessary x = array[i] y = array[i+1] if x > y: x, y = y, x # Compare the min with the smaller one and the max # with the bigger one minimum = min(x, minimum) maximum = max(y, maximum) i += 2 # If we had an even sized array we need to compare the # one remaining item too. if not odd: x = array[length] minimum = min(x, minimum) maximum = max(x, maximum) return minimum, maximumЭто определенно быстрее, чем наивный подход Peque представлены:
arr = np.random.random(3000000) assert minmax(arr) == minmax_peque(arr) # warmup and making sure they are identical %timeit minmax(arr) # 100 loops, best of 3: 2.1 ms per loop %timeit minmax_peque(arr) # 100 loops, best of 3: 2.75 ms per loopкак и ожидалось, новая реализация minmax занимает примерно 3/4 времени, которое заняла наивная реализация (
2.1 / 2.75 = 0.7636363636363637)
никто не упомянул numpy.процентиль, так я и думал. Если вы просите
[0, 100]процентили, это даст вам массив из двух элементов, мин (0-й процентиль) и макс (100-й процентиль).однако он не удовлетворяет цели OP: он не быстрее, чем min и max отдельно. Это, вероятно, связано с некоторыми механизмами, которые позволили бы использовать неэкстремальные процентили (более сложная проблема, которая должны принять более длительный.)
In [1]: import numpy In [2]: a = numpy.random.normal(0, 1, 1000000) In [3]: %%timeit ...: lo, hi = numpy.amin(a), numpy.amax(a) ...: 100 loops, best of 3: 4.08 ms per loop In [4]: %%timeit ...: lo, hi = numpy.percentile(a, [0, 100]) ...: 100 loops, best of 3: 17.2 ms per loop In [5]: numpy.__version__ Out[5]: '1.14.4'будущая версия Numpy может поместить в специальный случай, чтобы пропустить нормальное вычисление процентиля, если только
[0, 100]просят. Не добавляя ничего в интерфейс, есть способ попросить Numpy для min и max в одном вызове (вопреки тому, что было сказано в принятом ответе), но стандартная реализация библиотеки не использует этот случай, чтобы сделать его стоящим.
Comments