2010-09-07 19 views
10

Estoy trabajando con un 2D numpy masked_array en Python. Necesito cambiar los valores de datos en el área enmascarada de modo que sean iguales al valor desenmascarado más cercano.¿Completa los valores que faltan con el vecino más cercano en las matrices enmascaradas de Python nuked?

NB. Si hay más de un valor desenmascarado más cercano, puede tomar cualquiera de esos valores más cercanos (cualquiera que resulte más fácil de codificar ...)

p.

import numpy 
import numpy.ma as ma 

a = numpy.arange(100).reshape(10,10) 
fill_value=-99 
a[2:4,3:8] = fill_value 
a[8,8] = fill_value 
a = ma.masked_array(a,a==fill_value) 

>>> a [[0 1 2 3 4 5 6 7 8 9] 
    [10 11 12 13 14 15 16 17 18 19] 
    [20 21 22 -- -- -- -- -- 28 29] 
    [30 31 32 -- -- -- -- -- 38 39] 
    [40 41 42 43 44 45 46 47 48 49] 
    [50 51 52 53 54 55 56 57 58 59] 
    [60 61 62 63 64 65 66 67 68 69] 
    [70 71 72 73 74 75 76 77 78 79] 
    [80 81 82 83 84 85 86 87 -- 89] 
    [90 91 92 93 94 95 96 97 98 99]], 
  • necesito que se vea como esto:
>>> a.data 
[[0 1 2 3 4 5 6 7 8 9] 
[10 11 12 13 14 15 16 17 18 19] 
[20 21 22 ? 14 15 16 ? 28 29] 
[30 31 32 ? 44 45 46 ? 38 39] 
[40 41 42 43 44 45 46 47 48 49] 
[50 51 52 53 54 55 56 57 58 59] 
[60 61 62 63 64 65 66 67 68 69] 
[70 71 72 73 74 75 76 77 78 79] 
[80 81 82 83 84 85 86 87 ? 89] 
[90 91 92 93 94 95 96 97 98 99]], 

NB. dónde "?" podría tomar cualquiera de los valores adyacentes no enmascarados.

¿Cuál es la forma más eficiente de hacerlo?

Gracias por su ayuda.

Respuesta

9

Se podría utilizar para hacer copias np.roll desplazada de a, a continuación, utilizar la lógica booleana en las máscaras para identificar los puntos a rellenar:

import numpy as np 
import numpy.ma as ma 

a = np.arange(100).reshape(10,10) 
fill_value=-99 
a[2:4,3:8] = fill_value 
a[8,8] = fill_value 
a = ma.masked_array(a,a==fill_value) 
print(a) 

# [[0 1 2 3 4 5 6 7 8 9] 
# [10 11 12 13 14 15 16 17 18 19] 
# [20 21 22 -- -- -- -- -- 28 29] 
# [30 31 32 -- -- -- -- -- 38 39] 
# [40 41 42 43 44 45 46 47 48 49] 
# [50 51 52 53 54 55 56 57 58 59] 
# [60 61 62 63 64 65 66 67 68 69] 
# [70 71 72 73 74 75 76 77 78 79] 
# [80 81 82 83 84 85 86 87 -- 89] 
# [90 91 92 93 94 95 96 97 98 99]] 

for shift in (-1,1): 
    for axis in (0,1):   
     a_shifted=np.roll(a,shift=shift,axis=axis) 
     idx=~a_shifted.mask * a.mask 
     a[idx]=a_shifted[idx] 

print(a) 

# [[0 1 2 3 4 5 6 7 8 9] 
# [10 11 12 13 14 15 16 17 18 19] 
# [20 21 22 13 14 15 16 28 28 29] 
# [30 31 32 43 44 45 46 47 38 39] 
# [40 41 42 43 44 45 46 47 48 49] 
# [50 51 52 53 54 55 56 57 58 59] 
# [60 61 62 63 64 65 66 67 68 69] 
# [70 71 72 73 74 75 76 77 78 79] 
# [80 81 82 83 84 85 86 87 98 89] 
# [90 91 92 93 94 95 96 97 98 99]] 

Si desea utilizar una mayor conjunto de vecinos más cercanos, que tal vez podría hacer algo como esto:

neighbors=((0,1),(0,-1),(1,0),(-1,0),(1,1),(-1,1),(1,-1),(-1,-1), 
      (0,2),(0,-2),(2,0),(-2,0)) 

Tenga en cuenta que el orden de los elementos en neighbors es importante. Probablemente desee completar los valores que faltan con el vecino más cercano, no cualquier vecino. Probablemente haya una forma más inteligente de generar la secuencia de vecinos, pero no la estoy viendo en este momento.

a_copy=a.copy() 
for hor_shift,vert_shift in neighbors: 
    if not np.any(a.mask): break 
    a_shifted=np.roll(a_copy,shift=hor_shift,axis=1) 
    a_shifted=np.roll(a_shifted,shift=vert_shift,axis=0) 
    idx=~a_shifted.mask*a.mask 
    a[idx]=a_shifted[idx] 

Nota que np.roll rodillos felizmente el borde inferior a la parte superior, por lo que un valor que falta en la parte superior puede ser rellenado por un valor de la parte inferior. Si esto es un problema, tendría que pensar más sobre cómo solucionarlo. La solución obvia, pero no muy inteligente sería utilizar if declaraciones y alimentar a los bordes de una secuencia diferente de los vecinos admisibles ...

+0

Gran! Eso funciona para mis propósitos. Una pregunta: ¿se podría generalizar para trabajar con brechas de datos más grandes donde el valor desenmascarado más cercano está a más de un punto de distancia? –

+0

@Pete: una forma rápida de hacerlo es ajustar los bucles for en 'while np.any (a.mask):'. @unutbu - Por cierto, es una manera astuta de implementar la interpolación del vecino más cercano. –

+0

¡Gracias Joe! Mis felicitaciones me hacen muy feliz. :) – unutbu

5

Para los casos más complicados se puede utilizar scipy.spatial:

from scipy.spatial import KDTree 
x,y=np.mgrid[0:a.shape[0],0:a.shape[1]] 

xygood = np.array((x[~a.mask],y[~a.mask])).T 
xybad = np.array((x[a.mask],y[a.mask])).T 

a[a.mask] = a[~a.mask][KDTree(xygood).query(xybad)[1]] 

print a 
    [[0 1 2 3 4 5 6 7 8 9] 
    [10 11 12 13 14 15 16 17 18 19] 
    [20 21 22 13 14 15 16 17 28 29] 
    [30 31 32 32 44 45 46 38 38 39] 
    [40 41 42 43 44 45 46 47 48 49] 
    [50 51 52 53 54 55 56 57 58 59] 
    [60 61 62 63 64 65 66 67 68 69] 
    [70 71 72 73 74 75 76 77 78 79] 
    [80 81 82 83 84 85 86 87 78 89] 
    [90 91 92 93 94 95 96 97 98 99]] 
+0

¿Se podría utilizar este mismo enfoque para extrapolar fuera del casco convexo después de interpolar algunos datos espaciados irregularmente utilizando un algoritmo de Vecino más cercano? Parece que podría funcionar, pero tal vez haya mejores alternativas. Solo me pregunto, gracias. – SSZero

5

por lo general utilice una transformación de distancia, como sugirió sabiamente Juh_ en this question.

Esto no se aplica directamente a matrices enmascaradas, pero no creo que sea tan difícil de transportar allí, y es bastante eficiente, no he tenido problemas para aplicarlo a imágenes grandes de 100MPix.

Copiar el método relevante allí por referencia:

import numpy as np 
from scipy import ndimage as nd 

def fill(data, invalid=None): 
    """ 
    Replace the value of invalid 'data' cells (indicated by 'invalid') 
    by the value of the nearest valid data cell 

    Input: 
     data: numpy array of any dimension 
     invalid: a binary array of same shape as 'data'. True cells set where data 
       value should be replaced. 
       If None (default), use: invalid = np.isnan(data) 

    Output: 
     Return a filled array. 
    """ 
    #import numpy as np 
    #import scipy.ndimage as nd 

    if invalid is None: invalid = np.isnan(data) 

    ind = nd.distance_transform_edt(invalid, return_distances=False, return_indices=True) 
    return data[tuple(ind)] 
+0

Solución muy simple, gracias! –

Cuestiones relacionadas