Search code examples
matlaboctavereshapetiling

Reshape to vertically tile blocks of columns below previous ones


In my script I generate a matrix where each column is coupled with at least another one. For example, column 1 is coupled with column 2, column 3 is coupled with column 4, etc... But I could also couple columns 3 by 3 or 4 by 4 or any other number.

This is for the moment just an image in mind, but then I would like to move coupled columns on their own line, so that I can easily mix them up using any() or sum().

This will become clearer with this example:

A = reshape(1:12, 3, []) % A is the matrix I start with, this reshape is OK
A =

    1    4    7   10
    2    5    8   11
    3    6    9   12

reshape(A, [], 2) % this reshape is not OK
ans =

    1    7
    2    8
    3    9
    4   10
    5   11
    6   12

However, I would like the answer to be:

ans =

    1    4
    2    5
    3    6
    7   10
    8   11
    9   12

As I said, this example is just for 2 columns, but in my use case I also need to support any number of columns couples. Here for 3 columns:

B = reshape(1:18, 3, [])
B =

    1    4    7   10   13   16
    2    5    8   11   14   17
    3    6    9   12   15   18

reshape(B, [], 3)
ans =

    1    7   13
    2    8   14
    3    9   15
    4   10   16
    5   11   17
    6   12   18

What I would like:

ans =

    1    4   7
    2    5   8
    3    6   9
   10   13  16
   11   14  17
   12   15  18

Is there any way to do that in a vectorized manner?


Solution

  • Assuming M to be the input matrix, see if this works for you -

    ncols = 2; %// number of columns (needs to be edited)
    [m,n] = size(M) %// get size of input matrix for later usage
    r = numel(M)/(m*ncols);
    out = reshape(permute(reshape(M,m,ncols,[]),[1 3 2]),m*r,[])
    

    Sample runs -

    M =
         1     4     7    10
         2     5     8    11
         3     6     9    12
    ncols =
         2
    out =
         1     4
         2     5
         3     6
         7    10
         8    11
         9    12
    

    and

    M =
         1     4     7    10    13    16
         2     5     8    11    14    17
         3     6     9    12    15    18
    ncols =
         3
    out =
         1     4     7
         2     5     8
         3     6     9
        10    13    16
        11    14    17
        12    15    18
    

    Covering another possible intended question

    Going by your words - "column 1 is coupled with column 2, column 3 is coupled with column 4, etc... But I could also couple columns 3 by 3 or 4 by 4 or any other number", I am sensing you might actually be looking to form all possible combinations of the columns of the input matrix and vertically concatenate them to form a slenderish matrix as the output. This section of the solution would cover that base. The code to achieve such a goal (if that's what you meant hopefully) would be something like this -

    ncols = 2; %// number of columns (needs to be edited)
    [m,n] = size(M) %// get size of input matrix for later usage
    combs = dec2base(0:n^2-1,n,ncols)-'0'+1 %// find combinations
    combsp = permute(combs,[3 2 1]) %// make a 3D array of those combinations
    
    idx = bsxfun(@plus,[1:m]',(combsp-1)*m) %//'# Indices as a 3D array
    idx1 = reshape(permute(idx,[1 3 2]),m*size(idx,3),[]) %// vertically concatenate 
                                                %// 3D indices array into a 2D array
    out = M(idx1) %// desired output
    

    One sample run -

    M =
         6     7     3     6
         3     1     6     3
         5     1     4     2
         
    ncols = 2
    
    out =
         6     6
         3     3
         5     5
         6     7
         3     1
         5     1
         6     3
         3     6
         5     4
         6     6
         3     3 ....