Фильтр большинства Numpy массив - PullRequest
0 голосов
/ 13 февраля 2019

У меня есть nundy ndarray, состоящий из нулей, единиц и NaN.Я хотел бы использовать мажоритарный фильтр для этого массива, это означает, что я хотел бы установить окно ядра (например, ячейки 3X3), которое будет проходить по массиву и изменит значение ячейки в центре на значение, которое происходитбольше всего у соседей.Этот фильтр должен выдерживать два ограничения, он должен игнорировать NaN и, если значение центральной ячейки равно единице, то он должен сохранять это значение равным единице.

Вот небольшой пример того, что я ищу: входной массив:

array([[ 1.,  1.,  1.,  0.,  0.],
       [ 1.,  1., nan,  1.,  1.],
       [nan,  1.,  1.,  0.,  1.],
       [ 0.,  0.,  0.,  0.,  1.]])

Применить массив выходных данных фильтра большинства:

array([[ 1.,  1.,  1.,  1.,  1.],
       [ 1.,  1., nan,  1.,  1.],
       [nan,  1.,  1.,  1.,  1.],
       [ 0.,  0.,  0.,  1.,  1.]])

Я смотрел scipy filters но не смог найти ничего адекватного.Я думал построить универсальный свернутый фильтр , но я не уверен, как это сделать для большинства целей.Такое ощущение, что это базовый фильтр, который должен быть там, но я не могу его найти.

Ответы [ 2 ]

0 голосов
/ 13 февраля 2019

Вот одна векторизованная идея, основанная на convolution.Учитывая эти ограничения, кажется, что нам нужно редактировать только 0s мест.Для каждого скользящего окна получите число 1 с, а затем число, отличное от NaN, которое определяет порог для принятия решения о том, являются ли 1 с большинством.Если это так, установите те места, которые также равны 0, как 1 с.

Реализация будет выглядеть примерно так -

from scipy.signal import convolve2d

def fill0s(a):
    # Mask of NaNs
    nan_mask = np.isnan(a)

    # Convolution kernel
    k = np.ones((3,3),dtype=int)

    # Get count of 1s for each kernel window
    ones_count = convolve2d(np.where(nan_mask,0,a),k,'same')

    # Get count of elements per window and hence non NaNs count
    n_elem = convolve2d(np.ones(a.shape,dtype=int),k,'same')
    nonNaNs_count = n_elem - convolve2d(nan_mask,k,'same')

    # Compare 1s count against half of nonNaNs_count for the first mask.
    # This tells us if 1s are majority among non-NaNs population.
    # Second mask would be of 0s in a. Use Combined mask to set 1s.
    final_mask = (ones_count >= nonNaNs_count/2.0) & (a==0)
    return np.where(final_mask,1,a)

Обратите внимание, что с тех пор мы выполняем равномерную фильтрацию с такого рода1s ядро, мы также можем использовать uniform_filter.

Пример запуска -

In [232]: a
Out[232]: 
array([[ 1.,  1.,  1.,  0.,  0.],
       [ 1.,  1., nan,  1.,  1.],
       [nan,  1.,  1.,  0.,  1.],
       [ 0.,  0.,  0.,  0.,  1.]])

In [233]: fill0s(a)
Out[233]: 
array([[ 1.,  1.,  1.,  1.,  1.],
       [ 1.,  1., nan,  1.,  1.],
       [nan,  1.,  1.,  1.,  1.],
       [ 0.,  0.,  0.,  1.,  1.]])
0 голосов
/ 13 февраля 2019

Попробуйте следующий код:

Обратите внимание, что результат немного отличается от вашего, из-за поведения numpy.argmax, когда несколько индексов имеют одинаковое максимальное значение (вы можете написать свой собственный argmaxфункция ... x = np.argwhere (x == np.max (x)) [:, 0] выдает все индексы, а не только первый)

import numpy as np

def block_fn(x,center_val):

    unique_elements, counts_elements = np.unique(x.ravel(), return_counts=True)

    if np.isnan(center_val):
        return np.nan
    elif center_val == 1:
        return 1.0
    else:
        return unique_elements[np.argmax(counts_elements)]



def majority_filter(x,block_size = (3,3)):

    #Odd block sizes only  ( ? )
    assert(block_size[0]%2 != 0 and block_size[1]%2 !=0)

    yy =int((block_size[0]-1)/2)
    xx =int((block_size[1]-1)/2)


    output= np.zeros_like(x)
    for i in range(0,x.shape[0]):
        miny,maxy = max(0,i-yy),min(x.shape[0]-1,i+yy)

        for j in range(0,x.shape[1]):
            minx,maxx = max(0,j-xx),min(x.shape[1]-1,j+xx)

            #Extract block to take majority filter over
            block=x[miny:maxy+1,minx:maxx+1]

            output[i,j] = block_fn(block,center_val=x[i,j])


    return output


inp=np.array([[ 1.,  1.,  1.,  0.,  0.],
       [ 1.,  1., np.nan,  1.,  1.],
       [np.nan,  1.,  1.,  0.,  1.],
       [ 0.,  0.,  0.,  0.,  1.]])


print(majority_filter(inp))
...