问题描述
我想拥有一个可以检测局部最大值/最小值在数组中的位置的函数(即使存在一组局部最大值/最小值).示例:
I would like to have a function that can detect where the local maxima/minima are in an array (even if there is a set of local maxima/minima). Example:
给出数组
test03 = np.array([2,2,10,4,4,4,5,6,7,2,6,5,5,7,7,1,1])
我想要一个输出,如:
set of 2 local minima => array[0]:array[1]
set of 3 local minima => array[3]:array[5]
local minima, i = 9
set of 2 local minima => array[11]:array[12]
set of 2 local minima => array[15]:array[16]
从示例中可以看到,不仅检测到奇异值,还检测到局部最大值/最小值集.
As you can see from the example, not only are the singular values detected but, also, sets of local maxima/minima.
我在这个问题中知道有很多很好的答案和想法,但都没有完成所描述的工作:其中一些只是忽略了数组的极点,而全部都忽略了局部极小值/极大值的集合.
I know in this question there are a lot of good answers and ideas, but none of them do the job described: some of them simply ignore the extreme points of the array and all ignore the sets of local minima/maxima.
在问问题之前,我自己编写了一个函数,该函数的功能与我上面所述的完全相同(该函数位于问题的末尾:local_min(a)
.经过我的测试,它可以正常工作).
Before asking the question, I wrote a function by myself that does exactly what I described above (the function is at the end of this question: local_min(a)
. With the test I did, it works properly).
问题:但是,我也确信这不是使用Python的最佳方法.我可以使用内置的函数,API,库等吗?还有其他功能建议吗?单行指令?完整的矢量解决方案?
Question: However, I am also sure that is NOT the best way to work with Python. Are there builtin functions, APIs, libraries, etc. that I can use? Any other function suggestion? A one-line instruction? A full vectored solution?
def local_min(a):
candidate_min=0
for i in range(len(a)):
# Controlling the first left element
if i==0 and len(a)>=1:
# If the first element is a singular local minima
if a[0]<a[1]:
print("local minima, i = 0")
# If the element is a candidate to be part of a set of local minima
elif a[0]==a[1]:
candidate_min=1
# Controlling the last right element
if i == (len(a)-1) and len(a)>=1:
if candidate_min > 0:
if a[len(a)-1]==a[len(a)-2]:
print("set of " + str(candidate_min+1)+ " local minima => array["+str(i-candidate_min)+"]:array["+str(i)+"]")
if a[len(a)-1]<a[len(a)-2]:
print("local minima, i = " + str(len(a)-1))
# Controlling the other values in the middle of the array
if i>0 and i<len(a)-1 and len(a)>2:
# If a singular local minima
if (a[i]<a[i-1] and a[i]<a[i+1]):
print("local minima, i = " + str(i))
# print(str(a[i-1])+" > " + str(a[i]) + " < "+str(a[i+1])) #debug
# If it was found a set of candidate local minima
if candidate_min >0:
# The candidate set IS a set of local minima
if a[i] < a[i+1]:
print("set of " + str(candidate_min+1)+ " local minima => array["+str(i-candidate_min)+"]:array["+str(i)+"]")
candidate_min = 0
# The candidate set IS NOT a set of local minima
elif a[i] > a[i+1]:
candidate_min = 0
# The set of local minima is growing
elif a[i] == a[i+1]:
candidate_min = candidate_min + 1
# It never should arrive in the last else
else:
print("Something strange happen")
return -1
# If there is a set of candidate local minima (first value found)
if (a[i]<a[i-1] and a[i]==a[i+1]):
candidate_min = candidate_min + 1
更新:
有人建议导入from scipy.signal import argrelextrema
并使用如下功能:
Somebody was suggesting to import from scipy.signal import argrelextrema
and use the function like:
def local_min_scipy(a):
minima = argrelextrema(a, np.less_equal)[0]
return minima
def local_max_scipy(a):
minima = argrelextrema(a, np.greater_equal)[0]
return minima
拥有这样的东西是我真正想要的.但是,当局部最小值/最大值集具有两个以上的值时,它将无法正常工作.例如:
To have something like that is what I am really looking for. However, it doesn't work properly when the sets of local minima/maxima have more than two values. For example:
test03 = np.array([2,2,10,4,4,4,5,6,7,2,6,5,5,7,7,1,1])
print(local_max_scipy(test03))
输出为:
[ 0 2 4 8 10 13 14 16]
当然,在test03[4]
中,我有一个最小值而不是最大值.如何解决此问题? (我不知道这是另一个问题还是在哪里问这个问题.)
Of course in test03[4]
I have a minimum and not a maximum. How do I fix this behavior? (I don't know if this is another question or if this is the right place where to ask it.)
推荐答案
完整的矢量解决方案:
test03 = np.array([2,2,10,4,4,4,5,6,7,2,6,5,5,7,7,1,1]) # Size 17
extended = np.empty(len(test03)+2) # Rooms to manage edges, size 19
extended[1:-1] = test03
extended[0] = extended[-1] = np.inf
flag_left = extended[:-1] <= extended[1:] # Less than successor, size 18
flag_right = extended[1:] <= extended[:-1] # Less than predecessor, size 18
flagmini = flag_left[1:] & flag_right[:-1] # Local minimum, size 17
mini = np.where(flagmini)[0] # Indices of minimums
spl = np.where(np.diff(mini)>1)[0]+1 # Places to split
result = np.split(mini, spl)
result
:
[0, 1] [3, 4, 5] [9] [11, 12] [15, 16]
编辑
不幸的是,由于它们被视为平坦的局部极小值,因此一旦它们至少有3个项目,它也会立即检测出最大值.这样的numpy补丁会很丑.
Unfortunately, This detects also maxima as soon as they are at least 3 items large, since they are seen as flat local minima. A numpy patch will be ugly this way.
为解决此问题,我提出了另外两个解决方案,分别是numpy和numba.
To solve this problem I propose 2 other solutions, with numpy, then with numba.
使用np.diff
的numpy:
import numpy as np
test03=np.array([12,13,12,4,4,4,5,6,7,2,6,5,5,7,7,17,17])
extended=np.full(len(test03)+2,np.inf)
extended[1:-1]=test03
slope = np.sign(np.diff(extended)) # 1 if ascending,0 if flat, -1 if descending
not_flat,= slope.nonzero() # Indices where data is not flat.
local_min_inds, = np.where(np.diff(slope[not_flat])==2)
#local_min_inds contains indices in not_flat of beginning of local mins.
#Indices of End of local mins are shift by +1:
start = not_flat[local_min_inds]
stop = not_flat[local_min_inds+1]-1
print(*zip(start,stop))
#(0, 1) (3, 5) (9, 9) (11, 12) (15, 16)
与numba加速兼容的直接解决方案:
A direct solution compatible with numba acceleration :
#@numba.njit
def localmins(a):
begin= np.empty(a.size//2+1,np.int32)
end = np.empty(a.size//2+1,np.int32)
i=k=0
begin[k]=0
search_end=True
while i<a.size-1:
if a[i]>a[i+1]:
begin[k]=i+1
search_end=True
if search_end and a[i]<a[i+1]:
end[k]=i
k+=1
search_end=False
i+=1
if search_end and i>0 : # Final plate if exists
end[k]=i
k+=1
return begin[:k],end[:k]
print(*zip(*localmins(test03)))
#(0, 1) (3, 5) (9, 9) (11, 12) (15, 16)
这篇关于在1D-NumPy数组中查找局部最大值/最小值的奇异点/集(再次)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!