1

I am facing the following problem. I have an np.array, of the following structure:

[A, B, C, D, E, F]

where A..F are numpy arrays, guaranteed to be of the same size. I am hoping to achieve the following shape:

[ A | B, C | D, E | F ]

where A | B is np.hstack([A, B]). I would also like to be able to generalize this into any number of elements in the hstack, given that this number divides the length of this array.

I am not sure how to achieve that - there probably is some nice solution, but my experience doesn't lead me there. I would appreciate some insight.

Jytug
  • 1,072
  • 2
  • 11
  • 29
  • If `a = np.array([[1,2,3,4],[5,6,7,8]]); b = np.array([[11,12,13,14],[21,22,23,24]]);` then `np.hstack([a,b])` produces: `array([[ 1, 2, 3, 4, 11, 12, 13, 14], [ 5, 6, 7, 8, 21, 22, 23, 24]])` Is this what you want? – AGN Gazer Sep 12 '17 at 16:39
  • 1
    What exactly is the input array shape with respect to each individual array? `[A, B, C, D, E, F]` doesn't exactly give us that info, unless you mean : `input_arr = np.array([A, B, C, D, E, F])`. Is this what you have? – Divakar Sep 12 '17 at 16:41
  • I could have specified my purpose in the post. `A..F` are images imported by `PIL`, and I want to tile six (or more) images into a `2x3` grid and save it as an image – Jytug Sep 12 '17 at 17:01
  • So, is the input a list of those images/arrays, or an array? If array, again what's the shape corresponding to each image/array? Or would you always have exactly 6 images? – Divakar Sep 12 '17 at 17:04
  • The shape of the array is `(6, 1000, 1000, 4)` and I want an array of `(2000, 3000, 4)` – Jytug Sep 12 '17 at 17:22
  • Shouldn't it be `(3000, 2000, 4)` because its horizontal stacking? – Divakar Sep 12 '17 at 17:56
  • Yes, that is correct – Jytug Sep 12 '17 at 21:24

2 Answers2

2

A manual way to compose a NumPy array out of smaller blocks is to use np.block or np.bmat:

h, w, N = 3, 4, 6
A, B, C, D, E, F = [np.full((h,w), i) for i in list('ABCDEF')]
result = np.block([[A, B], [C, D], [E, F]])

yields

array([['A', 'A', 'A', 'A', 'B', 'B', 'B', 'B'],
       ['A', 'A', 'A', 'A', 'B', 'B', 'B', 'B'],
       ['A', 'A', 'A', 'A', 'B', 'B', 'B', 'B'],
       ['C', 'C', 'C', 'C', 'D', 'D', 'D', 'D'],
       ['C', 'C', 'C', 'C', 'D', 'D', 'D', 'D'],
       ['C', 'C', 'C', 'C', 'D', 'D', 'D', 'D'],
       ['E', 'E', 'E', 'E', 'F', 'F', 'F', 'F'],
       ['E', 'E', 'E', 'E', 'F', 'F', 'F', 'F'],
       ['E', 'E', 'E', 'E', 'F', 'F', 'F', 'F']],
      dtype='<U1')

Assuming the blocks are 2D arrays, a more automated way to reshape a NumPy array of blocks is to use unblockshaped:

import numpy as np

def unblockshaped(arr, h, w):
    """
    http://stackoverflow.com/a/16873755/190597 (unutbu)
    Return an array of shape (h, w) where
    h * w = arr.size

    If arr is of shape (n, nrows, ncols), n sublocks of shape (nrows, ncols),
    then the returned array preserves the "physical" layout of the sublocks.
    """
    n, nrows, ncols = arr.shape
    return (arr.reshape(h // nrows, -1, nrows, ncols)
               .swapaxes(1, 2)
               .reshape(h, w))

h, w, N = 3, 4, 6
arr = np.array([np.full((h,w), i) for i in list('ABCDEF')])
result = unblockshaped(arr, h*N//2, w*2)
print(result)

which produces the same result.


See this question for an example of how to arrange a sequence of images into a grid.

unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
0

It is not completely clear what is the final product that you desire. It is something like this?

import numpy as np

a = np.array([
    [1, 2, 3], [2, 3, 4], [5, 6, 7], 
    [8, 9, 10], [11, 12, 13], [14, 15, 16]])

def function(a_in, size):
    size_2 = size * a.shape[1]
    if a.shape[0] % size == 0:
        return np.reshape(a, (a.shape[0] // size, size_2))
    raise RuntimeError("Sorry I can't")


print("a = {}".format(a))
print("b(2) = {}".format(function(a, 2)))
print("b(3) = {}".format(function(a, 3)))

it prints:

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

b(2) = [[ 1  2  3  2  3  4]
 [ 5  6  7  8  9 10]
 [11 12 13 14 15 16]]

b(3) = [[ 1  2  3  2  3  4  5  6  7]
 [ 8  9 10 11 12 13 14 15 16]]
Matteo Ragni
  • 2,837
  • 1
  • 20
  • 34