I am trying to split a 2D numpy array that is not square into nonoverlapping chunks of smaller 2D numpy arrays. Example - split a 3x4 array into chunks of 2x2:
The array to be split:
[[ 34 15 16 17]
[ 78 98 99 100]
[ 23 78 79 80]]
This should output:
[[34 15]
[78 98]]
[[ 16 17]
[ 99 100]]
So [23 78 79 80]
are dropped because they do not match the 2x2 requirement.
My current code is this:
new_array = np.array([[34,15,16,17], [78,98,99,100], [23,78,79,80]])
window = 2
for x in range(0, new_array.shape[0], window):
for y in range(0, new_array.shape[1], window):
patch_im1 = new_array[x:x window,y:y window]
This outputs:
[[34 15]
[78 98]]
[[ 16 17]
[ 99 100]]
[[23 78]]
[[79 80]]
Ideally, I would like to have the chunks stored in a list.
CodePudding user response:
Not sure of all possible cases (input array sizes) you may have in your problem, but this approach should be flexible to work with any 2D input size and any chunk shape. It utilizes view_as_blocks
from the skimage
library to get non-overlapping views of an array.
import numpy as np
from skimage.util.shape import view_as_blocks
new_array = np.array([[34,15,16,17], [78,98,99,100], [23,78,79,80]])
First, you need to trim the original array to get a size that is evenly divisible by the shape of your desired chunks. So, this 3x4
array will become a 2x4
array when we remove the last row.
chunk_shape = (2,2)
chunk_rows, chunk_cols = chunk_shape
rows_to_keep = new_array.shape[0] - new_array.shape[0] % chunk_rows
cols_to_keep = new_array.shape[1] - new_array.shape[1] % chunk_cols
temp = new_array[:rows_to_keep, :cols_to_keep]
print(temp)
# [[34 15 16 17]
# [78 98 99 100]]
Now, we can use the view_as_blocks
function to obtain the chunks of desired size and convert the result to a list of lists as you want:
res = view_as_blocks(temp, chunk_shape).reshape(-1, np.prod(chunk_shape)).tolist()
print(res)
# [[34, 15, 78, 98], [16, 17, 99, 100]]
CodePudding user response:
This should work on any number of dimensions. Let's take an array:
new_array = np.array(range(25)).reshape((5,5))
Output:
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24]])
First calculate the number of last rows/columns which you don't need:
N = 2
rem = np.array(new_array.shape) % N
Output:
array([1, 1])
Then remove the number of rows/columns from the end of your array on each dimension:
for ax, v in enumerate(rem):
if v != 0:
new_array = np.delete(new_array, range(-1, -v-1, -1), axis=ax)
Output:
array([[ 0, 1, 2, 3],
[ 5, 6, 7, 8],
[10, 11, 12, 13],
[15, 16, 17, 18]])
Then use np.split
on each dimension:
arr_list = [new_array]
for ax in range(len(x.shape)):
arr_list_list = [np.split(arr, arr.shape[ax] / N, axis=ax) for arr in arr_list]
arr_list = [arr for j in arr_list_list for arr in j]
Output:
[array([[0, 1],
[5, 6]]),
array([[2, 3],
[7, 8]]),
array([[10, 11],
[15, 16]]),
array([[12, 13],
[17, 18]])]
Then transform into a list:
[list(i.reshape(i.size)) for i in arr_list]
Output:
[[0, 1, 5, 6], [2, 3, 7, 8], [10, 11, 15, 16], [12, 13, 17, 18]]