Reshape to vertically tile blocks of columns below previous ones

942 views Asked by At

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?

2

There are 2 answers

4
Divakar On BEST ANSWER

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 ....    
0
gaborous On

Divakar's solution is the best one, but if like me you use sparse matrices and Octave, Octave just doesn't support yet N-D sparse matrices, so you can't reshape like Divakar did:

First, although it is fundamentally possible to have N-dimensional sparse objects, the Octave sparse classes do not allow them at this time; All instances of the sparse classes must be 2-dimensional. This means that SparseMatrix is actually more similar to Octave’s Matrix class than its NDArray class.

Source: GNU Octave's documentation

The only workaround is to use a loop-based solution, like this function:

function B = vertical_tile(A, ncols)

B = [];
if issparse(A)
    B = sparse(B);
end

for i=1:ncols
    B = [B A(:, i:ncols:end)];
end
B = reshape(B, [], ncols);

end

Results:

vertical_tile(A, 2)
ans =

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

vertical_tile(B, 3)
ans =

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