Заполните пропущенные значения ближайшим соседом в массивах с масками Python? - PullRequest
12 голосов
/ 08 сентября 2010

Я работаю с 2D Numpy masked_array в Python. Мне нужно изменить значения данных в замаскированной области так, чтобы они равнялись ближайшему немаскированному значению.

NB. Если имеется более одного ближайшего немаскированного значения, то оно может принимать любое из этих ближайших значений (которое когда-либо оказывается наиболее простым для кодирования…)

, например

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]],
  • Мне нужно, чтобы это выглядело так:
>>> 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. где "?" может принимать любое из смежных немаскированных значений.

Какой самый эффективный способ сделать это?

Спасибо за вашу помощь.

Ответы [ 3 ]

10 голосов
/ 08 сентября 2010

Вы можете использовать np.roll для создания сдвинутых копий a, а затем использовать булеву логику для масок, чтобы определить места, которые нужно заполнить:

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

Если вы хотите использовать больший набор ближайших соседей, вы можете сделать что-то вроде этого:

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

Обратите внимание, что порядок элементов в neighbors важен. Возможно, вы захотите заполнить пропущенные значения с помощью ближайшего соседа , а не просто любого соседа. Вероятно, есть более разумный способ генерации последовательности соседей, но в данный момент я ее не вижу.

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]

Обратите внимание, что np.roll успешно скатывает нижний край к вершине, поэтому пропущенное значение в верхней части может быть заполнено значением с самого низа. Если это проблема, мне нужно больше думать о том, как ее исправить. Очевидным, но не очень умным решением было бы использовать операторы if и задавать ребрам другую последовательность допустимых соседей ...

8 голосов
/ 02 января 2015

Я обычно использую дистанционное преобразование, как мудро предложено Юхом в этом вопросе .

Это не относится напрямую к замаскированным массивам, но я не думаю, что это будет так сложнотранспонировать туда, и это довольно эффективно, у меня не было проблем с применением его к большим 100-мегапиксельным изображениям.

Копирование соответствующего метода туда для справки:

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)]
5 голосов
/ 14 марта 2012

Для более сложных случаев вы можете использовать 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]]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...