Reshape / Transform an upper triangular matrix in MATLAB - 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:
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:

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.

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;

Alternate option, using loops:
[nRows nCols]= size(M);
R = zeros(nRows,nCols);
for n = 1:nRows
R((n+1):nCols,n)=fliplr(M(n,(n+1):nCols));
end

Related

Generating unitriangular matrices in Octave|MATLAB [duplicate]

Suppose I have an NxN matrix A, an index vector V consisting of a subset of the numbers 1:N, and a value K, and I want to do this:
for i = V
A(i,i) = K
end
Is there a way to do this in one statement w/ vectorization?
e.g. A(something) = K
The statement A(V,V) = K will not work, it assigns off-diagonal elements, and this is not what I want. e.g.:
>> A = zeros(5);
>> V = [1 3 4];
>> A(V,V) = 1
A =
1 0 1 1 0
0 0 0 0 0
1 0 1 1 0
1 0 1 1 0
0 0 0 0 0
I usually use EYE for that:
A = magic(4)
A(logical(eye(size(A)))) = 99
A =
99 2 3 13
5 99 10 8
9 7 99 12
4 14 15 99
Alternatively, you can just create the list of linear indices, since from one diagonal element to the next, it takes nRows+1 steps:
[nRows,nCols] = size(A);
A(1:(nRows+1):nRows*nCols) = 101
A =
101 2 3 13
5 101 10 8
9 7 101 12
4 14 15 101
If you only want to access a subset of diagonal elements, you need to create a list of diagonal indices:
subsetIdx = [1 3];
diagonalIdx = (subsetIdx-1) * (nRows + 1) + 1;
A(diagonalIdx) = 203
A =
203 2 3 13
5 101 10 8
9 7 203 12
4 14 15 101
Alternatively, you can create a logical index array using diag (works only for square arrays)
diagonalIdx = false(nRows,1);
diagonalIdx(subsetIdx) = true;
A(diag(diagonalIdx)) = -1
A =
-1 2 3 13
5 101 10 8
9 7 -1 12
4 14 15 101
>> tt = zeros(5,5)
tt =
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
>> tt(1:6:end) = 3
tt =
3 0 0 0 0
0 3 0 0 0
0 0 3 0 0
0 0 0 3 0
0 0 0 0 3
and more general:
>> V=[1 2 5]; N=5;
>> tt = zeros(N,N);
>> tt((N+1)*(V-1)+1) = 3
tt =
3 0 0 0 0
0 3 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 3
This is based on the fact that matrices can be accessed as one-dimensional arrays (vectors), where the 2 indices (m,n) are replaced by a linear mapping m*N+n.
>> B=[0,4,4;4,0,4;4,4,0]
B =
0 4 4
4 0 4
4 4 0
>> v=[1,2,3]
v =
1 2 3
>> B(eye(size(B))==1)=v
%insert values from v to eye positions in B
B =
1 4 4
4 2 4
4 4 3
A = zeros(7,6);
V = [1 3 5];
[n m] = size(A);
diagIdx = 1:n+1:n*m;
A( diagIdx(V) ) = 1
A =
1 0 0 0 0 0
0 0 0 0 0 0
0 0 1 0 0 0
0 0 0 0 0 0
0 0 0 0 1 0
0 0 0 0 0 0
0 0 0 0 0 0
I'd use sub2ind and pass the diagonal indices as both x and y parameters:
A = zeros(4)
V=[2 4]
idx = sub2ind(size(A), V,V)
% idx = [6, 16]
A(idx) = 1
% A =
% 0 0 0 0
% 0 1 0 0
% 0 0 0 0
% 0 0 0 1
Suppose K is the value. The command
A=A-diag(K-diag(A))
may be a bit faster
>> A=randn(10000,10000);
>> tic;A(logical(eye(size(A))))=12;toc
Elapsed time is 0.517575 seconds.
>> tic;A=A+diag((99-diag(A)));toc
Elapsed time is 0.353408 seconds.
But it consumes more memory.
I use this small inline function in finite difference code.
A=zeros(6,3);
range=#(A,i)[1-min(i,0):size(A,1)-max(i+size(A,1)-size(A,2),0 ) ];
Diag=#(A,i) sub2ind(size(A), range(A,i),range(A,i)+i );
A(Diag(A, 0))= 10; %set diagonal
A(Diag(A, 1))= 20; %equivelent to diag(A,1)=20;
A(Diag(A,-1))=-20; %equivelent to diag(A,-1)=-20;
It can be easily modified to work on a sub-range of the diagonal by changing the function range.

Diagonal matrix in matlab

I am having trouble creating this matrix in matlab, basically I need to create a matrix that has -1 going across the center diagonal followed be 4s on the diagonal outside of that (example below). All the other values can be zero.
A5 = [-1 4 0 0 0;
4 -1 4 0 0;
0 4 -1 4 0;
0 0 4 -1 4;
0 0 0 4 -1];
I have tried using a command v = [4]; D = diag(v)
but that only works for the center diagonal.
This can also be done using a toeplitz matrix:
function out = tridiag(a,b,c,N)
% TRIDIAG generates a tri-diagonal matrix of size NxN.
% lower diagonal is a
% main diagonal is b
% upper diagonal is c
out = toeplitz([b,a,zeros(1,N-2)],[b,c,zeros(1,N-2)]);
>> tridiag(4,-1,4,5)
ans =
-1 4 0 0 0
4 -1 4 0 0
0 4 -1 4 0
0 0 4 -1 4
0 0 0 4 -1
Note #1: When your desired output is symmetric, you can omit the 2nd input to toeplitz.
Note #2: As the size of the matrix increases, there comes a point where it makes more sense to store it as sparse, as this saves memory and improves performance (assuming your matrix is indeed sparse, i.e. comprised mostly of zeros, as it happens with a tridiagonal matrix). Some useful functions are spdiags, sptoeplitzFEX and blktridiagFEX.
A little hackish, but here it goes:
N = 7; % matrix size
v = [11 22 33]; % row vector containing the diagonal values
w = [0 v(end:-1:1)];
result = w(max(numel(v)+1-abs(bsxfun(#minus, 1:N, (1:N).')),1))
This gives
result =
11 22 33 0 0 0 0
22 11 22 33 0 0 0
33 22 11 22 33 0 0
0 33 22 11 22 33 0
0 0 33 22 11 22 33
0 0 0 33 22 11 22
0 0 0 0 33 22 11
To understand how it works, see some intermediate steps:
>> abs(bsxfun(#minus, 1:N, (1:N).'))
ans =
0 1 2 3 4 5 6
1 0 1 2 3 4 5
2 1 0 1 2 3 4
3 2 1 0 1 2 3
4 3 2 1 0 1 2
5 4 3 2 1 0 1
6 5 4 3 2 1 0
>> max(numel(v)+1-abs(bsxfun(#minus, 1:N, (1:N).')),1)
ans =
4 3 2 1 1 1 1
3 4 3 2 1 1 1
2 3 4 3 2 1 1
1 2 3 4 3 2 1
1 1 2 3 4 3 2
1 1 1 2 3 4 3
1 1 1 1 2 3 4
Use D = diag(u,k) to shift u in k levels above the main diagonal, and D = diag(u,-k) for the opposite direction. Keep in mind that you need u to be in the right length of the k diagonal you want, so if the final matrix is n*n, the k's diagonal will have only n-abs(k) elements.
For you case:
n = 5; % the size of the matrix
v = ones(n,1)-2; % make the vector for the main diagonal
u = ones(n-1,1)*4; % make the vector for +1 and -1 diagonal
A5 = diag(v)+diag(u,1)+diag(u,-1) % combine everything together
Which gives:
A5 =
-1 4 0 0 0
4 -1 4 0 0
0 4 -1 4 0
0 0 4 -1 4
0 0 0 4 -1

How to normalize matrix setting 0 for minimum values and 1 for maximum values?

I need to transform a neural network output matrix with size 2 X N in zeros and ones, where 0 will represent the minimum value of the column and 1 contrariwise. This will be necessary in order to calculate the confusion matrix.
For example, consider this matrix 2 X 8:
2 33 4 5 6 7 8 9
1 44 5 4 7 5 2 1
I need to get this result:
1 0 0 1 0 1 1 1
0 1 1 0 1 0 0 0
How can I do this in MATLAB without for loops? Thanks in advance.
>> d = [ 2 33 4 5 6 7 8 9;
1 44 5 4 7 5 2 1];
>> bsxfun(#rdivide, bsxfun(#minus, d, min(d)), max(d) - min(d))
ans =
1 0 0 1 0 1 1 1
0 1 1 0 1 0 0 0
The bsxfun function is necessary to broadcast the minus and division operations to matrices of different dimensions (min and max have only 1 row each).
Other solution is the following (works only for 2 rows):
>> [d(1,:) > d(2,:); d(1,:) < d(2,:)]
ans =
1 0 0 1 0 1 1 1
0 1 1 0 1 0 0 0
If it's just 2xN, then this will work:
floor(A./[max(A); max(A)])
In general:
floor(A./repmat(max(A),size(A,1),1))

Loopless submatrix assignment in Matlab

I have a matrix F of size D-by-N and a vector A of length N of random integers in the range [1,a]. I want to create a matrix M of size D * a such that each colum M(:,i) has the vector F(:,i) starting from the index (A(i)-1)*D+1 to (A(i)-1)*D+D.
Example:
F = [1 2 3 10
4 5 6 22]
A = [3 2 1 2]
a = 4
M = [0 0 3 0
0 0 6 0
0 2 0 10
0 5 0 22
1 0 0 0
4 0 0 0
0 0 0 0
0 0 0 0]
I can do it with a simple loop
for i = 1 : N
M((A(i)-1)*D+1:(A(i)-1)*D+D,i) = F(:,i);
end
but for large N this might take a while. I am looking for a way to do it without loop.
You can use bsxfun for a linear-indexing based approach -
[D,N] = size(F); %// Get size of F
start_idx = (A-1)*D+1 + [0:N-1]*D*a; %// column start linear indices
all_idx = bsxfun(#plus,start_idx,[0:D-1]'); %//'# all linear indices
out = zeros(D*a,N); %// Initialize output array with zeros
out(all_idx) = F; %// Insert values from F into output array
Sample run -
F =
1 2 3 10
4 5 6 22
A =
3 2 1 2
a =
4
out =
0 0 3 0
0 0 6 0
0 2 0 10
0 5 0 22
1 0 0 0
4 0 0 0
0 0 0 0
0 0 0 0

How to assign values to a MATLAB matrix on the diagonal?

Suppose I have an NxN matrix A, an index vector V consisting of a subset of the numbers 1:N, and a value K, and I want to do this:
for i = V
A(i,i) = K
end
Is there a way to do this in one statement w/ vectorization?
e.g. A(something) = K
The statement A(V,V) = K will not work, it assigns off-diagonal elements, and this is not what I want. e.g.:
>> A = zeros(5);
>> V = [1 3 4];
>> A(V,V) = 1
A =
1 0 1 1 0
0 0 0 0 0
1 0 1 1 0
1 0 1 1 0
0 0 0 0 0
I usually use EYE for that:
A = magic(4)
A(logical(eye(size(A)))) = 99
A =
99 2 3 13
5 99 10 8
9 7 99 12
4 14 15 99
Alternatively, you can just create the list of linear indices, since from one diagonal element to the next, it takes nRows+1 steps:
[nRows,nCols] = size(A);
A(1:(nRows+1):nRows*nCols) = 101
A =
101 2 3 13
5 101 10 8
9 7 101 12
4 14 15 101
If you only want to access a subset of diagonal elements, you need to create a list of diagonal indices:
subsetIdx = [1 3];
diagonalIdx = (subsetIdx-1) * (nRows + 1) + 1;
A(diagonalIdx) = 203
A =
203 2 3 13
5 101 10 8
9 7 203 12
4 14 15 101
Alternatively, you can create a logical index array using diag (works only for square arrays)
diagonalIdx = false(nRows,1);
diagonalIdx(subsetIdx) = true;
A(diag(diagonalIdx)) = -1
A =
-1 2 3 13
5 101 10 8
9 7 -1 12
4 14 15 101
>> tt = zeros(5,5)
tt =
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
>> tt(1:6:end) = 3
tt =
3 0 0 0 0
0 3 0 0 0
0 0 3 0 0
0 0 0 3 0
0 0 0 0 3
and more general:
>> V=[1 2 5]; N=5;
>> tt = zeros(N,N);
>> tt((N+1)*(V-1)+1) = 3
tt =
3 0 0 0 0
0 3 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 3
This is based on the fact that matrices can be accessed as one-dimensional arrays (vectors), where the 2 indices (m,n) are replaced by a linear mapping m*N+n.
>> B=[0,4,4;4,0,4;4,4,0]
B =
0 4 4
4 0 4
4 4 0
>> v=[1,2,3]
v =
1 2 3
>> B(eye(size(B))==1)=v
%insert values from v to eye positions in B
B =
1 4 4
4 2 4
4 4 3
A = zeros(7,6);
V = [1 3 5];
[n m] = size(A);
diagIdx = 1:n+1:n*m;
A( diagIdx(V) ) = 1
A =
1 0 0 0 0 0
0 0 0 0 0 0
0 0 1 0 0 0
0 0 0 0 0 0
0 0 0 0 1 0
0 0 0 0 0 0
0 0 0 0 0 0
I'd use sub2ind and pass the diagonal indices as both x and y parameters:
A = zeros(4)
V=[2 4]
idx = sub2ind(size(A), V,V)
% idx = [6, 16]
A(idx) = 1
% A =
% 0 0 0 0
% 0 1 0 0
% 0 0 0 0
% 0 0 0 1
Suppose K is the value. The command
A=A-diag(K-diag(A))
may be a bit faster
>> A=randn(10000,10000);
>> tic;A(logical(eye(size(A))))=12;toc
Elapsed time is 0.517575 seconds.
>> tic;A=A+diag((99-diag(A)));toc
Elapsed time is 0.353408 seconds.
But it consumes more memory.
I use this small inline function in finite difference code.
A=zeros(6,3);
range=#(A,i)[1-min(i,0):size(A,1)-max(i+size(A,1)-size(A,2),0 ) ];
Diag=#(A,i) sub2ind(size(A), range(A,i),range(A,i)+i );
A(Diag(A, 0))= 10; %set diagonal
A(Diag(A, 1))= 20; %equivelent to diag(A,1)=20;
A(Diag(A,-1))=-20; %equivelent to diag(A,-1)=-20;
It can be easily modified to work on a sub-range of the diagonal by changing the function range.