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.min
与minmax1
和minmax2
仍然是一场失败的战斗,所以这不仅仅是一个记忆问题…
笔记 – 将尺寸增加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
)