Reputation: 3681
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
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