Slice 2D Array into Smaller 2D Arrays

Slice 2d array into smaller 2d arrays

There was another question a couple of months ago which clued me in to the idea of using reshape and swapaxes. The h//nrows makes sense since this keeps the first block's rows together. It also makes sense that you'll need nrows and ncols to be part of the shape. -1 tells reshape to fill in whatever number is necessary to make the reshape valid. Armed with the form of the solution, I just tried things until I found the formula that works.

You should be able to break your array into "blocks" using some combination of reshape and swapaxes:

def blockshaped(arr, nrows, ncols):
"""
Return an array of shape (n, nrows, ncols) where
n * nrows * ncols = arr.size

If arr is a 2D array, the returned array should look like n subblocks with
each subblock preserving the "physical" layout of arr.
"""
h, w = arr.shape
assert h % nrows == 0, f"{h} rows is not evenly divisible by {nrows}"
assert w % ncols == 0, f"{w} cols is not evenly divisible by {ncols}"
return (arr.reshape(h//nrows, nrows, -1, ncols)
.swapaxes(1,2)
.reshape(-1, nrows, ncols))

turns c

np.random.seed(365)
c = np.arange(24).reshape((4, 6))
print(c)

[out]:
[[ 0 1 2 3 4 5]
[ 6 7 8 9 10 11]
[12 13 14 15 16 17]
[18 19 20 21 22 23]]

into

print(blockshaped(c, 2, 3))

[out]:
[[[ 0 1 2]
[ 6 7 8]]

[[ 3 4 5]
[ 9 10 11]]

[[12 13 14]
[18 19 20]]

[[15 16 17]
[21 22 23]]]

I've posted an inverse function, unblockshaped, here, and an N-dimensional generalization here. The generalization gives a little more insight into the reasoning behind this algorithm.


Note that there is also superbatfish's
blockwise_view. It arranges the
blocks in a different format (using more axes) but it has the advantage of (1)
always returning a view and (2) being capable of handling arrays of any
dimension.

How do I split a 2D array into smaller 2D arrays, of variable size?

This builds the generator you need.

from itertools import product
import numpy as np

n, m = 70, 85
a = np.arange(n * m).reshape(n, m)

def igen(a, n, m):
i_ = np.arange(a.shape[0]) // n
j_ = np.arange(a.shape[1]) // m
for i, j in product(np.unique(i_), np.unique(j_)):
yield (i, j), a[i_ == i][:, j_ == j]

dict_of_arrays = dict(igen(a, 16, 16))

another alternative
pad with np.nan and reshape + transpose

def cover_multiple(current_length, multiple):
return ((current_length - 1) // multiple + 1) * multiple

def slicer(a, chunk_i, chunk_j, two_d=True):
n = cover_multiple(a.shape[0], chunk_i)
m = cover_multiple(a.shape[1], chunk_j)
c = np.empty((n, m))
c.fill(np.nan)
c[:a.shape[0], :a.shape[1]] = a
c = c.reshape(n // chunk_i, chunk_i, m // chunk_j, chunk_j)
c = c.transpose(0, 2, 1, 3)
if not two_d:
c = c.reshape(-1, chunk_i, chunk_j)
return c

demo

a = np.arange(64).reshape(8, 8)
a

[[ 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 25 26 27 28 29 30 31]
[32 33 34 35 36 37 38 39]
[40 41 42 43 44 45 46 47]
[48 49 50 51 52 53 54 55]
[56 57 58 59 60 61 62 63]]

print(slicer(a, 3, 5))

[[[[ 0. 1. 2. 3. 4.]
[ 8. 9. 10. 11. 12.]
[ 16. 17. 18. 19. 20.]]

[[ 5. 6. 7. nan nan]
[ 13. 14. 15. nan nan]
[ 21. 22. 23. nan nan]]]

[[[ 24. 25. 26. 27. 28.]
[ 32. 33. 34. 35. 36.]
[ 40. 41. 42. 43. 44.]]

[[ 29. 30. 31. nan nan]
[ 37. 38. 39. nan nan]
[ 45. 46. 47. nan nan]]]

[[[ 48. 49. 50. 51. 52.]
[ 56. 57. 58. 59. 60.]
[ nan nan nan nan nan]]

[[ 53. 54. 55. nan nan]
[ 61. 62. 63. nan nan]
[ nan nan nan nan nan]]]]

Slice a 2D array into an array of smaller 'blocks' or 'chunks'

arr = [ [ 'A', 'B', 1, 2, 'α', 'β' ],

[ 'C', 'D', 3, 4, 'θ', 'μ' ],

[ 'E', 'F', 5, 6, 'ω', 'ξ' ] ], l = console.log, j = JSON.stringify



l(j( arr[0].slice(2, 4) )) // "[1,2]" - slice can be used to get part of array

l(j( arr.map(a => a.slice(2, 4)) )) // "[[1,2],[3,4],[5,6]]" - map + slice to get part of each sub-array

result = [0, 2, 4].map(i => arr.map(a => a.slice(i, i+2))) // map over the indexes to split by to get the parts

l(j( arr )), l(j( result ))

Numpy: How to slice or split 2D subsections of 2D array

In [2]: ary = np.arange(24).reshape(6,4)
In [3]: ary
Out[3]:
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]])

While I recommended moving-windows based on as_strided, we can also divide the array into 'blocks' with reshape and transpose:

In [4]: ary.reshape(3,2,2,2).transpose(0,2,1,3)
Out[4]:
array([[[[ 0, 1],
[ 4, 5]],

[[ 2, 3],
[ 6, 7]]],

[[[ 8, 9],
[12, 13]],

[[10, 11],
[14, 15]]],

[[[16, 17],
[20, 21]],

[[18, 19],
[22, 23]]]])
In [5]: np.sqrt(_.sum(axis=(2,3)))
Out[5]:
array([[3.16227766, 4.24264069],
[6.4807407 , 7.07106781],
[8.60232527, 9.05538514]])

While the transpose makes it easier to visual the blocks that need to be summed, it isn't necessary:

In [7]: np.sqrt(ary.reshape(3,2,2,2).sum(axis=(1,3)))
Out[7]:
array([[3.16227766, 4.24264069],
[6.4807407 , 7.07106781],
[8.60232527, 9.05538514]])

np.lib.stride_tricks.sliding_window doesn't give us as much direct control as I thought, but

np.lib.stride_tricks.sliding_window_view(ary,(2,2))[::2,::2]

gives the same result as Out[4].

In [13]: np.sqrt(np.lib.stride_tricks.sliding_window_view(ary,(2,2))[::2,::2].sum(axis=(2,3)))
Out[13]:
array([[3.16227766, 4.24264069],
[6.4807407 , 7.07106781],
[8.60232527, 9.05538514]])

[7] is faster.

In general, it can be done like this:

a_height = 15
a_width = 16
a_area = a_height * a_width
a = np.arange(a_are).reshape(a_height, a_width)

window_height = 3 # must evenly divide a_height
window_width = 4 # must evenly divide a_width

b_height = a_height // window_height
b_width = a_width // window_width

b = a.reshape(b_height, window_height, b_width, window_width).transpose(0,2,1,3)

# or, assuming you want sum or another function that takes `axis` argument
b = a.reshape(b_height, window_height, b_width, window_width).sum(axis=(1,3))

c# How do i split a 2D array into a list of smaller 2D arrays (chunks)?

you can achieve this using Buffer.BlockCopy and little logic.
Check the below code

static void Main(string[] args)
{
int p = 1;
int[,] array = new int[9, 9];
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
array[i, j] = p++;
}
}
GetChunkUsingBlockCopy(array, 3, 3);
}

static List<int[,]> GetChunkUsingBlockCopy(int[,] array, int row, int column)
{
int chunkcount = (array.GetLength(0) * array.GetLength(1)) / (row * column);
List<int[,]> chunkList = new List<int[,]>();
int[,] chunk = new int[row, column];

var byteLength = sizeof(int) * chunk.Length;
for (int i = 0; i < chunkcount; i++)
{
chunk = new int[row, column];
Buffer.BlockCopy(array, byteLength * i, chunk, 0, byteLength);

chunkList.Add(chunk);
}

return chunkList;
}

Hope this helps !
Please do not forget to mark this as Answer if found suitable.

Chunk 2D array into smaller arrays, get the chunk means, and plot a heatmap

Main Array

  • Remove linewidth
  • Add set_xticklabels and set_yticklabels
# test data
np.random.seed(365)
data = np.random.random((1080,1920))

ax = sns.heatmap(data, cmap='jet')
ax.set_xticks(xticks) # this is only the tick location, not the label
ax.set_xticklabels(xticks) # this adds the labels, after setting the ticks
ax.set_yticks(yticks)
ax.set_yticklabels(yticks)

ax.invert_yaxis() # use if desired to swap the direction of the y-axis values
ax.grid(color='k')

plt.show()

Sample Image

Divide the Array

  • I used the function in this answer to chunk the data into an array (288, 90, 80)
# using function from other answer
chunked = blockshaped(data, 90, 80)

# get the means of each chunk and then reshape
means = np.array([v.mean() for v in chunked]).reshape(12, 24)

# plot the chunks
fig, ax = plt.subplots(figsize= (16,9))

p = sns.heatmap(means, cmap='jet', ax=ax)

p.set_xticks(range(25))
p.set_xticklabels([0] + xticks)
p.set_yticks(range(13))
p.set_yticklabels([0] + yticks)

p.invert_yaxis()
p.grid(color='k')

plt.show()

Sample Image

blockshaped

  • Here's the function from the other answer for reshapeding the array
def blockshaped(arr, nrows, ncols):
"""
Return an array of shape (n, nrows, ncols) where
n * nrows * ncols = arr.size

If arr is a 2D array, the returned array should look like n subblocks with
each subblock preserving the "physical" layout of arr.
"""
h, w = arr.shape
assert h % nrows == 0, f"{h} rows is not evenly divisible by {nrows}"
assert w % ncols == 0, f"{w} cols is not evenly divisible by {ncols}"
return (arr.reshape(h//nrows, nrows, -1, ncols)
.swapaxes(1,2)
.reshape(-1, nrows, ncols))


Related Topics



Leave a reply



Submit