Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reshape / Transform an upper triangular matrix in MATLAB

I have an upper triangular matrix (without the diagonal) given by:

M = [0 3 2 2 0 0; 0 0 8 6 3 2; 0 0 0 3 2 1; 0 0 0 0 2 1; 0 0 0 0 0 0]

The resulting matrix should look like this:

R = [0 0 0 0 0 0; 0 2 0 0 0 0; 2 3 1 0 0 0; 2 6 2 1 0 0; 3 8 3 2 0 0]

Since I couldn't find a simple explanation which describes my goal I tried to visualize it with an image:

enter image description here

I already tried lots of different combinations of rot90, transpose, flipud etc., but I could't find the right transformation that gives me the matrix R

EDIT:

The rows of the matrix M are not always sorted as in the example above. For another matrix M_2:

M_2 = [0 2 3 1 0 0; 0 0 3 6 3 9; 0 0 0 1 2 4; 0 0 0 0 2 6; 0 0 0 0 0 0]

the resulting matrix R_2 need to be the following:

R_2 = [0 0 0 0 0 0; 0 9 0 0 0 0; 1 3 4 0 0 0; 3 6 2 6 0 0; 2 3 1 2 0 0]

Again the visualization below:

enter image description here

like image 274
Schnigges Avatar asked Dec 26 '22 22:12

Schnigges


2 Answers

EDIT: Inspired by the tip from @Dan's comment, it can be further simplified to

R = reshape(rot90(M), size(M));

Original Answer:

This should be a simple way to do this

F = rot90(M);
R = F(reshape(1:numel(M), size(M)))

which returns

R =
     0     0     0     0     0     0
     0     2     0     0     0     0
     2     3     1     0     0     0
     2     6     2     1     0     0
     3     8     3     2     0     0

The idea is that when you rotate the matrix you get

>> F = rot90(M)
F =
     0     2     1     1     0
     0     3     2     2     0
     2     6     3     0     0
     2     8     0     0     0
     3     0     0     0     0
     0     0     0     0     0

which is a 6 by 5 matrix. If you consider the linear indexing over F the corresponding indices are

>> reshape(1:30, size(F))
     1     7    13    19    25
     2     8    14    20    26
     3     9    15    21    27
     4    10    16    22    28
     5    11    17    23    29
     6    12    18    24    30

where elements 6, 11, 12, 16, 17, 18 , and ... are zero now if you reshape this to a 5 by 6 matrix you get

>> reshape(1:30, size(M))
     1     6    11    16    21    26
     2     7    12    17    22    27
     3     8    13    18    23    28
     4     9    14    19    24    29
     5    10    15    20    25    30

Now those elements corresponding to zero values are on top, exactly what we wanted. So by passing this indexing array to F we get the desired R.

like image 198
Mohsen Nosratinia Avatar answered Jan 14 '23 14:01

Mohsen Nosratinia


Without relying on order (just rotating the colored strips and pushing them to the bottom).

First solution: note that it doesn't work if there are zeros between the "data" values (for example, if M(1,3) is 0 in the example given). If there may be zeros please see second solution below:

[nRows nCols]= size(M);
R = [flipud(M(:,2:nCols).') zeros(nRows,1)];
[~, rowSubIndex] = sort(~~R);
index = sub2ind([nRows nCols],rowSubIndex,repmat(1:nCols,nRows,1));
R = R(index);

Second solution: works even if there are zeros within the data:

[nRows nCols]= size(M);
S = [flipud(M(:,2:nCols).') zeros(nRows,1)];
mask = 1 + fliplr(tril(NaN*ones(nRows, nCols)));
S = S .* mask;
[~, rowSubIndex] = sort(~isnan(S));
index = sub2ind([nRows nCols],rowSubIndex,repmat(1:nCols,nRows,1));
R = S(index);
R(isnan(R)) = 0;
like image 33
Luis Mendo Avatar answered Jan 14 '23 13:01

Luis Mendo