2

Is there a similar function to scipy.ndimage's uniform_filter or convolve (similar problem with Numpy Two-Dimensional Moving Average), but the average is calculated using only the values in the input array (disregard count of fill values for corner and edge cells). A similar function wherein the fill value can be set to np.nan and the resulting average is calculated as np.nanmean?

My initial code loops over the array to get the neighbors and calculate the resulting mean, but this method takes too long. I have tried both uniform_filter or convolve, but the results are not what I needed since the resulting corner and edge values are too low (due to filling the edges with 0).

For example, if I have the array:

a = np.ones((4,5))

Calculating the mean value from a moving 3x3 array should also result to:

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

uniform_filter gives:

uniform_filter(a, size=3, mode='constant', cval=0.0)

array([[0.44444444, 0.66666667, 0.66666667, 0.66666667, 0.44444444],
       [0.66666667, 1.        , 1.        , 1.        , 0.66666667],
       [0.66666667, 1.        , 1.        , 1.        , 0.66666667],
       [0.44444444, 0.66666667, 0.66666667, 0.66666667, 0.44444444]])

I have tried setting cval=np.nan but the resulting values of the cell edges are nan.

For another array, b

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

I want to obtain an array, using a 3x3 averaging-window, as

array([[0.25      , 0.16666667, 0.        ],
       [0.16666667, 0.11111111, 0.        ],
       [0.        , 0.        , 0.        ]])

The values are calculated as shown in this illustration

Divakar
  • 218,885
  • 19
  • 262
  • 358
gsm17
  • 63
  • 4

2 Answers2

1

Try changing the code as

uniform_filter(a, size=3, mode='wrap')

This should avoid the problem of padding with zeros for the edge values

Check out this for other modes which you might prefer: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.uniform_filter.html

1

Get the windowed summations and divide by the valid members in each window. We can use scipy.signal.convolve2d to get both and hence have a solution like so -

from scipy.signal import convolve2d

def windowed_average(a, kernel_size, mode='same'):
    k = np.ones((kernel_size,kernel_size),dtype=int)
    window_sum = convolve2d(a,k,mode)
    window_count = convolve2d(np.ones(a.shape, dtype=bool),k,mode)
    return window_sum/window_count

Alternative #1

Alternatively, if you want to make use of uniform_filter to get the windowed summations, we can do so and that might be more efficient as well, like so -

from scipy.ndimage import uniform_filter

n = kernel_size**2
window_sum = uniform_filter(a, kernel_size, mode='constant', cval=0.0)*n

Sample runs -

In [54]: a
Out[54]: 
array([[1., 1., 1., 1., 1.],
       [1., 1., 1., 1., 1.],
       [1., 1., 1., 1., 1.],
       [1., 1., 1., 1., 1.]])

In [55]: windowed_average(a, kernel_size=3)
Out[55]: 
array([[1., 1., 1., 1., 1.],
       [1., 1., 1., 1., 1.],
       [1., 1., 1., 1., 1.],
       [1., 1., 1., 1., 1.]])

In [56]: b
Out[56]: 
array([[1., 0., 0.],
       [0., 0., 0.],
       [0., 0., 0.]])

In [57]: windowed_average(b, kernel_size=3)
Out[57]: 
array([[0.25      , 0.16666667, 0.        ],
       [0.16666667, 0.11111111, 0.        ],
       [0.        , 0.        , 0.        ]])
Divakar
  • 218,885
  • 19
  • 262
  • 358