How to find the local minima of a smooth multidimensional array in NumPy efficiently?
假设我有一个numpy数组,其中包含对连续可微函数的计算,我想找到局部极小值。没有噪音,所以每一个值低于所有相邻点的值的点都符合我对局部最小值的标准。
我有以下列表理解,它适用于二维数组,忽略边界上的潜在极小值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import numpy as N def local_minima(array2d): local_minima = [ index for index in N.ndindex(array2d.shape) if index[0] > 0 if index[1] > 0 if index[0] < array2d.shape[0] - 1 if index[1] < array2d.shape[1] - 1 if array2d[index] < array2d[index[0] - 1, index[1] - 1] if array2d[index] < array2d[index[0] - 1, index[1]] if array2d[index] < array2d[index[0] - 1, index[1] + 1] if array2d[index] < array2d[index[0], index[1] - 1] if array2d[index] < array2d[index[0], index[1] + 1] if array2d[index] < array2d[index[0] + 1, index[1] - 1] if array2d[index] < array2d[index[0] + 1, index[1]] if array2d[index] < array2d[index[0] + 1, index[1] + 1] ] return local_minima |
然而,这是相当缓慢的。我还想让它适用于任何数量的维度。例如,是否有一种简单的方法可以在任何维度的数组中获取点的所有相邻点?或者我是完全错误地处理这个问题?我应该用
对于任意维的数组,可以找到局部极小值的位置。使用Ivan的Detect_Peaks函数,稍作修改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | import numpy as np import scipy.ndimage.filters as filters import scipy.ndimage.morphology as morphology def detect_local_minima(arr): # https://stackoverflow.com/questions/3684484/peak-detection-in-a-2d-array/3689710#3689710 """ Takes an array and detects the troughs using the local maximum filter. Returns a boolean mask of the troughs (i.e. 1 when the pixel's value is the neighborhood maximum, 0 otherwise) """ # define an connected neighborhood # http://www.scipy.org/doc/api_docs/SciPy.ndimage.morphology.html#generate_binary_structure neighborhood = morphology.generate_binary_structure(len(arr.shape),2) # apply the local minimum filter; all locations of minimum value # in their neighborhood are set to 1 # http://www.scipy.org/doc/api_docs/SciPy.ndimage.filters.html#minimum_filter local_min = (filters.minimum_filter(arr, footprint=neighborhood)==arr) # local_min is a mask that contains the peaks we are # looking for, but also the background. # In order to isolate the peaks we must remove the background from the mask. # # we create the mask of the background background = (arr==0) # # a little technicality: we must erode the background in order to # successfully subtract it from local_min, otherwise a line will # appear along the background border (artifact of the local minimum filter) # http://www.scipy.org/doc/api_docs/SciPy.ndimage.morphology.html#binary_erosion eroded_background = morphology.binary_erosion( background, structure=neighborhood, border_value=1) # # we obtain the final mask, containing only peaks, # by removing the background from the local_min mask detected_minima = local_min - eroded_background return np.where(detected_minima) |
可以这样使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | arr=np.array([[[0,0,0,-1],[0,0,0,0],[0,0,0,0],[0,0,0,0],[-1,0,0,0]], [[0,0,0,0],[0,-1,0,0],[0,0,0,0],[0,0,0,-1],[0,0,0,0]]]) local_minima_locations = detect_local_minima(arr) print(arr) # [[[ 0 0 0 -1] # [ 0 0 0 0] # [ 0 0 0 0] # [ 0 0 0 0] # [-1 0 0 0]] # [[ 0 0 0 0] # [ 0 -1 0 0] # [ 0 0 0 0] # [ 0 0 0 -1] # [ 0 0 0 0]]] |
这表明最小值出现在指数[0,0,3]、[0,4,0]、[1,1,1]和[1,3,3]处:
1 2 3 4 | print(local_minima_locations) # (array([0, 0, 1, 1]), array([0, 4, 1, 3]), array([3, 0, 1, 3])) print(arr[local_minima_locations]) # [-1 -1 -1 -1] |
尝试此二维:
1 2 3 4 5 6 7 | import numpy as N def local_minima(array2d): return ((array2d <= N.roll(array2d, 1, 0)) & (array2d <= N.roll(array2d, -1, 0)) & (array2d <= N.roll(array2d, 1, 1)) & (array2d <= N.roll(array2d, -1, 1))) |
这将返回一个类似array2d的数组,其中包含本地minima(四个邻居)所在的true/false。