7

Digamos que tengo una matriz en NumPy que contiene evaluaciones de una función diferenciable continua, y quiero encontrar las mínimas locales. No hay ruido, por lo que cada punto cuyo valor es inferior a los valores de todos sus vecinos cumple mi criterio para un mínimo local.¿Cómo encontrar los mínimos locales de una matriz multidimensional suave en NumPy de manera eficiente?

que tienen la siguiente lista por comprensión, que trabaja para una matriz de dos dimensiones, haciendo caso omiso de potencial en los límites mínimos:

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 

Sin embargo, esto es bastante lento. También me gustaría hacer que esto funcione en cualquier cantidad de dimensiones. Por ejemplo, ¿hay una manera fácil de obtener todos los vecinos de un punto en una matriz de cualquier dimensión? ¿O me estoy acercando a este problema por completo? ¿Debería usar el numpy.gradient()?

+0

Encontrando los máximos globales: http://stackoverflow.com/questions/3584243/python-get-the-position-of-the-biggest-item-in-an-numpy-array/3584260#3584260 – endolith

Respuesta

14

La ubicación de los mínimos locales se puede encontrar una variedad de dimensión arbitraria usando Ivan 's detect_peaks function, con modificaciones menores:

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)  

que se puede utilizar de esta manera:

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]]] 

Esto dice que los mínimos se producen en los índices [0,0,3], [0,4,0], [1,1,1] y [1,3,3]:

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] 
+0

¡Agradable! Funciona alrededor de 65 veces más rápido que mi original, y funciona para cualquier cantidad de dimensiones. – ptomato

3

probar esto por 2D:

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))) 

Esto le va a devolver una matriz array2d-como con los mínimos locales donde Verdadero/Falso (cuatro vecinos) se encuentran.

+0

Bueno, esto en realidad encuentra máximos locales, y necesita '&' 's en lugar de '&&' 's, y requiere paréntesis alrededor de las comparaciones, pero se ejecuta treinta veces más rápido que mi original. – ptomato

+0

@ptomato - tienes razón, corregido ahora, gracias. – eumiro

Cuestiones relacionadas