numpy:函数同时max()和min()

numpy.amax()将在数组中find最大值,而numpy.amin()对最小值也是一样的。 如果我想find最大和最小,我必须调用这两个函数,这需要两次(非常大)的数组,这似乎很慢。

在numpy API中是否有一个函数,可以通过数据只查找一个最大值和最小值?

我不认为两次传递数组是一个问题。 考虑下面的伪代码:

 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中实现,所以循环或多或less都是免费的)。


编辑对不起4你谁upvoted和信任我。 你绝对可以优化这个。

这里有一些fortran代码,可以通过f2py编译成一个python模块(也许一个Cython guru可以将其与优化的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 

现在我们正在一个可以testing的地方:

 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.minminmax1minmax2仍然是一场失败的战斗,所以这不仅仅是一个记忆问题…

笔记 – 将尺寸增加10**a倍,然后重复减less10**a倍(保持问题的大小不变)确实改变了性能,但不是以一种看起来一致的方式,表明存在一些相互作用在python中的内存性能和函数调用开销之间。 即使比较fortran节拍numpy的一个简单的min实现的因素约2 …

在numpy API中是否有一个函数可以通过数据只发送一次数据就能find最大值和最小值?

在撰写本文时,没有这样的function。 (是的,如果有这样一个函数,它的性能会比在一个大arrays上相继调用numpy.amin()numpy.amax() 。)

有一个函数用于查找名为numpy.ptp的 (max-min),如果这对你有用的话:

 >>> import numpy >>> x = numpy.array([1,2,3,4,5,6]) >>> x.ptp() 5 

但我不认为有一种方法可以通过一次遍历find最小值和最大值。

编辑: PTT只是调用引擎盖下的最小和最大值

你可以使用Numba ,这是一个使用LLVM的支持NumPy的dynamicPython编译器。 由此产生的实现非常简单明了:

 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代码行。

做你自己的性能testing,因为它总是依赖于你的架构,你的数据,你的软件包版本…

这是一个古老的线索,但无论如何,如果有人再次看到这个…

同时查找最小值和最大值时,可以减less比较次数。 如果是浮点数(我猜是这样),这可能会节省一些时间,虽然不是计算复杂度。

而不是(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()

一般来说,您可以通过一次处理两个元素来减lessminmaxalgorithm的比较量,只将较小值与临时最小值进行比较,将较大值与临时最大值进行比较。 平均而言,只需要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