Navid777
Navid777

Reputation: 3681

numpy replace array elements with average of 2*2 blocks

I have an m*n matrix in numpy. I want to divide my matrix into 2*2 blocks and then replace each element with average of the elements in its block. for example consider the following array:

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

I want to produce this matrix:

[
    [3.5, 3.5, 5.5, 5.5]
    [3.5, 3.5, 5.5, 5.5]
    [11.5, 11.5, 13.5, 13.5]
    [11.5, 11.5, 13.5, 13.5]
]

What is the most efficient way to do this? Should I use for loops?

Upvotes: 5

Views: 970

Answers (1)

Divakar
Divakar

Reputation: 221614

One approach would be to reshape splitting each of those two axes into two more and find mean along the latter of those two, giving us the average values. We will keep the dimensions with keepdims=True, so as to facilitate replicating along the reduced axes later on with np.repeat for the final output.

Thus, one implementation would be -

b = a.reshape(2,2,2,2).mean((1,3), keepdims=1)
out = np.repeat(np.repeat(b,(2),axis=(1)),(2), axis=3).reshape(4,4)

Sample run -

In [17]: a
Out[17]: 
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12],
       [13, 14, 15, 16]])

In [18]: b = a.reshape(2,2,2,2).mean((1,3), keepdims=1)

In [19]: np.repeat(np.repeat(b,(2),axis=(1)),(2), axis=3).reshape(4,4)
Out[19]: 
array([[  3.5,   3.5,   5.5,   5.5],
       [  3.5,   3.5,   5.5,   5.5],
       [ 11.5,  11.5,  13.5,  13.5],
       [ 11.5,  11.5,  13.5,  13.5]])

Generically put, the solution would look like this -

m,n = a.shape
p,q = (2,2) # Block size
b = a.reshape(m//p,p,n//q,q).mean((1,3), keepdims=1)
out = np.repeat(np.repeat(b,(p),axis=(1)),(q), axis=3).reshape(a.shape)

Performance boost

We could replace that replicating part with an initialization based section, like so -

out = np.empty((m//p,p,n//q,q),dtype=float)
out[:] = b
out.shape = a.shape

Upvotes: 5

Related Questions