s28
s28

Reputation: 187

Python: Average Values in 2D-Array

I want to generate a twodimensional array in Python and I would like to iterate through each element and take an average. An element i should be averaged using the 8 surrounding array elements (including element i).

I generated the twodimensional array with a frame of zeros using Forming a frame of zeros around a matrix in python.

A = np.array([[1,2,3],[4,5,6],[7,8,9]])
x,y = A.shape
n = 1    
B = np.zeros((x+2*n,y+2*n),dtype=int)
B[n:x+n, n:y+n] = A
print(B)

What is the easiest way to take the average?

Upvotes: 2

Views: 209

Answers (3)

mozway
mozway

Reputation: 262224

What you want is a 2D convolution, use scipy.signal.convolve2d with numpy.ones to get the sum:

from scipy.signal import convolve2d

out = convolve2d(B, np.ones((3, 3), dtype='int'), mode='same')

Output:

array([[ 1,  3,  6,  5,  3],
       [ 5, 12, 21, 16,  9],
       [12, 27, 45, 33, 18],
       [11, 24, 39, 28, 15],
       [ 7, 15, 24, 17,  9]])

If you want the small matrix, no need to pad with zeros:

convolve2d(A, np.ones((3, 3), dtype='int'), mode='same')

# array([[12, 21, 16],
#        [27, 45, 33],
#        [24, 39, 28]])

For the average, repeat the same operation with an array od ones and divide:

kernel = np.ones((3, 3), dtype='int')
out = (convolve2d(A, kernel, mode='same')
      /convolve2d(np.ones_like(A), kernel, mode='same')
      )

output:

array([[3. , 3.5, 4. ],
       [4.5, 5. , 5.5],
       [6. , 6.5, 7. ]])

Upvotes: 2

Stefan Vučković
Stefan Vučković

Reputation: 126

This can be done relatively simply using a 3x3 kernel (filter) on your array, and making sure to also correctly check how many neighbours each element actually has (think edges and corners), before dividing to find the average.

from scipy.ndimage import convolve
import numpy as np

array = ...

kernel = np.array([[1,1,1],
                   [1,1,1],
                   [1,1,1]])

sum_neighbour = convolve(array, kernel, mode='constant', cval=0.0)

neighbour_count = convolve(np.ones_like(array), kernel, mode='constant', cval=0.0)

avg = sum_neighbour / neighbour_count

ex:

a matrix:

1,2,3,4
5,6,7,8
9,10,11,12,
13,14,15,16

has neighbour count:

4,6,6,4
6,9,9,6
6,9,9,6
4,6,6,4

sum:

14, 24, 30, 22
33, 54, ...

avg:

3.5, 4, 5, 5.5
5.5, 6.0, ...

Upvotes: 2

Ryan
Ryan

Reputation: 1223

Get each submatrix, and take the average. Store results in a separate matrix (otherwise the previously calculated average would mess up the calculation for the next average):

Code:

import numpy as np

A = np.array([[1,2,3],[4,5,6],[7,8,9]])
x,y = A.shape
n = 1    
B = np.zeros((x+2*n,y+2*n),dtype=float)
B[n:x+n, n:y+n] = A

C = B.copy() # not doing it in-place because the math would get messed up
for i in range(x):
    for j in range(y):
        sub_b = B[i:i+x, j:j+y]
        average = np.sum(sub_b) / 9
        C[i + n, j + n] = average
        
print(C)

Output:

[[0.         0.         0.         0.         0.        ]
 [0.         1.33333333 2.33333333 1.77777778 0.        ]
 [0.         3.         5.         3.66666667 0.        ]
 [0.         2.66666667 4.33333333 3.11111111 0.        ]
 [0.         0.         0.         0.         0.        ]]

Upvotes: 1

Related Questions