Create new matrix based on diagonal and antidiagonal of given matrix - matlab

I want to create matrix B from matrix A, with the following rules:
Non-diagonal elements A are like non-diagonal elements of B.
The main diagonal of A is the antidiagonal of B
The antidiagonal of A is the main diagonal of B.
For example:
A = [ 1 2 3 4;
7 8 9 10;
13 14 15 16;
19 20 21 22 ];
B = [ 4 2 3 1;
7 9 8 10;
13 15 14 16;
22 20 21 19 ];
How can I create B given A?

You can create all of the indices, then it's a single assignment.
% Get size of square matrix A
n = size(A,1);
% Indicies are 1:n^2 by default
idx = 1:n^2;
% Swap diagonal and antidiagonal indices
idx( [1:(n+1):n^2, n^2-n+1:1-n:n] ) = [n^2-n+1:1-n:n, 1:(n+1):n^2];
% Use the indexing array to create B from A, reshape to be n*n
B = reshape( A( idx ), n, n );
Output for your example A:
B =
4 2 3 1
7 9 8 10
13 15 14 16
22 20 21 19

There are so many ways to reach that results, it is just an indexing exercise. Here is one (of the many) way to reach that result for any square matrix of size n:
%% input
A=[ 1 2 3 4 ;
7 8 9 10 ;
13 14 15 16 ;
19 20 21 22 ];
%% Calculate linear indices for the diagonal and antidiagonal
n=size(A,1) ;
idxdiag = 1:(n+1):n^2 ; % => idxdiag = [1 6 11 16]
idxantidiag = n:(n-1):n^2-1 ; % => idxantidiag = [4 7 10 13]
%% Generate B
B = A ; % start with a simple copy (for the non-diagonal elements)
% Method 1: direct indice assignment
B(idxdiag) = diag(fliplr(A)) ; % Assign diagonal elements of B
B(idxantidiag) = flipud(diag(A)) ; % Assign antidiagonal elements of B
% Method 2: summation
B([idxdiag idxantidiag]) = 0 ;
B = B + diag(diag(fliplr(A))) + fliplr(diag(diag(A))) ;
B =
4 2 3 1
7 9 8 10
13 15 14 16
22 20 21 19
Both methods return exactly the same matrix B.
I suggest you familiarise yourself with the MATLAB function used to understand what is going on behind the scene:
fliplr
flipud
diag
and may be have a read at:
Matrix Indexing in MATLAB

I thought a little differently and came to a conclusion
A=[1 2 3 4;7 8 9 10;13 14 15 16; 19 20 21 22];;
n=size(A,1) ;
B=zeros(n,n) ;
for i=1:n
for j=1:n
if i==j
B(i,j)=A(i,n-i+1);
elseif j==n-i+1
B(i,j)=A(i,i);
else
B(i,j)=A(i,j);
end
end
end
B

Here's a variant using eye, find, and flip to generate linear indices:
ind1 = find(eye(size(A)));
ind2 = flip(find(flip(eye(size(A)))));
B = A;
B([ind1 ind2]) = B([ind2 ind1]);
B =
4 2 3 1
7 9 8 10
13 15 14 16
22 20 21 19
And here's a variant of the above that uses just eye and flip to generate logical indices:
ind1 = eye(size(A), 'logical');
ind2 = flip(ind1);
B = A;
B(ind1) = flip(A(ind2));
B(ind2) = flip(A(ind1));
B =
4 2 3 1
7 9 8 10
13 15 14 16
22 20 21 19

Related

Combine index-based and logical addressing in Matlab

Consider a matrix X. I have to update a submatrix of X, X(row1:row2, col1:col2), with a matrix Z (of size row2-row1+1, col2-col1+1) but only on those positions where a logical matrix L (of size row2-row1+1, col2-col1+1) is true.
E.g. if
X=[ 1 2 3 4 5 6
11 12 13 14 15 16
21 22 23 24 25 26
31 32 33 34 34 36]
Z=[31 41
32 42]
L=[ 1 0
0 1]
row1 = 2; row2 = 3; col1 = 3; col2 = 4
then after the update I should get:
X=[ 1 2 3 4 5 6
11 12 31 14 15 16
21 22 23 42 25 26
31 32 33 34 34 36]
Currently I do the following:
Y = X(row1:row2, col1:col2);
Y(L) = Z(L);
X(row1:row2, col1:col2) = Y;
This code is in a tight loop and according to Matlab's (v2019a) profiler is the main bottleneck of my program. In the real code X is a 2000x1500x3 cube; row1, row2, col1, col2, Z and L change in the loop.
The question is whether it can be rewritten into a single / faster assignment.
Thanks.
Honestly, without seeing your actual code, I get the sense that your solution may be as fast as you can get. The reason I say that is because I tested a few different solutions by creating some random sample data closer to your actual problem. I assumed X is an image of type uint8 with size 2000-by-1500-by-3, Z is size N-by-N (i.e. we will only be modifying the first page of X), L is an N-by-N logical array, and the row and column indices are randomly chosen:
X = randi([0 255], 2000, 1500, 3, 'uint8');
N = 20; % Submatrix size
Z = randi([0 255], N, N, 'uint8');
L = (rand(N, N) > 0.5);
row1 = randi([1 2000-N]);
row2 = row1+N-1
col1 = randi([1 1500-N]);
col2 = col1+N-1;
I then tested 3 different solutions: your original solution, a solution using find and sub2ind to create a linear index for X, and a solution that creates a logical index for X:
% Original solution:
Y = X(row1:row2, col1:col2, 1);
Y(L) = Z(L);
X(row1:row2, col1:col2, 1) = Y;
% Linear index solution:
[rIndex, cIndex] = find(L);
X(sub2ind(size(X), rIndex+row1-1, cIndex+col1-1)) = Z(L);
% Logical index solution
[R, C, ~] = size(X);
fullL = false(R, C);
fullL(row1:row2, col1:col2) = L;
X(fullL) = Z(L);
I tested these repeatedly with randomly-generated sample data using timeit and found that your original solution is consistently the fastest. The linear index solution is very close, but slightly slower. The logical index solution takes more than twice as long.
Let's define some example data:
X = randi(9,5,6);
Y = 10+X;
row1 = 2;
row2 = 4;
col1 = 3;
col2 = 4;
L = logical([0 1; 0 0; 1 1]);
Then:
ind_subm = bsxfun(#plus, (row1:row2).',size(X,1)*((col1:col2)-1));
% linear index for submatrix
ind_subm_masked = ind_subm(L);
% linear index for masked submatrix
X(ind_subm_masked) = Y(ind_subm_masked);
Example results:
X before:
X =
6 2 1 7 9 6
3 3 3 5 5 7
6 3 8 6 5 4
7 4 1 3 3 4
2 5 9 5 5 9
L:
L =
3×2 logical array
0 1
0 0
1 1
X after:
X =
6 2 1 7 9 6
3 3 3 15 5 7
6 3 8 6 5 4
7 4 11 13 3 4
2 5 9 5 5 9

Vectorising a Matlab code to pick specific indices of a matrix

I have a matrix A in Matlab of dimension Nx(N-1), e.g.
N=5;
A=[1 2 3 4;
5 6 7 8;
9 10 11 12;
13 14 15 16;
17 18 19 20];
I want to rearrange the elements of A in a certain way. Specifically I want to create a matrix B of dimension (N-1)xN such that:
for i=1,...,N,
B(:,i) collects
1) the first i-1 elements of the i-1th column of A and
2) the last N-i elements of the ith column of A.
Notice that for i=1 the i-1th column of A does not exist and therefore 1) is skipped; similarly, for i=N theith column of A does not exist and therefore 2) is skipped.
In the example above
B=[5 1 2 3 4
9 10 6 7 8
13 14 15 11 12
17 18 19 20 16];
This code does what I want. I am asking your help to vectorise it in an efficient way.
B=zeros(N-1,N);
for i=1:N
if i>1 && i<N
step1=A(1:i-1,i-1);
step2=A(i+1:N,i);
B(:,i)=[step1;step2];
elseif i==1
B(:,i)=A(i+1:N,i);
elseif i==N
B(:,i)=A(1:i-1,i-1);
end
end
Extract the lower and upper triangular matrices of A. Then reassemble them with a "diagonal shift":
u = triu(A);
l = tril(A,-1);
B = padarray(u(1:end-1,:),[0 1],'pre') + padarray(l(2:end,:),[0 1],'post');
Another valid approach using logical indexing combined with tril and triu:
B = zeros(size(A'));
B(tril(true(size(B)))) = A(tril(true(size(A)), -1));
B(triu(true(size(B)), 1)) = A(triu(true(size(A))));
Result:
>> B
B =
5 1 2 3 4
9 10 6 7 8
13 14 15 11 12
17 18 19 20 16

How to efficiently reshape one column matrix to many specific length columns by moving specific interval

The input is an N-by-1 matrix. I need to reshape it to L-by-M matrix. The following is an example.
Input:
b =
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Set length = 18, Output:
X =
1 2 3
2 3 4
3 4 5
4 5 6
5 6 7
6 7 8
7 8 9
8 9 10
9 10 11
10 11 12
11 12 13
12 13 14
13 14 15
14 15 16
15 16 17
16 17 18
17 18 19
18 19 20
Because I have a very big matrix, using a loop to reshape is very inefficient. How can I improve the reshape speed?
Your example output matrix X is the perfect matrix to index a vector of length N to get what you want. It's also very easy to create using bsxfun:
N = 20;
b = rand(N,1);
M = 3; %// number of columns
L = N-M; %// Note that N-M is an upper limit for L!
idx = bsxfun(#plus, (0:L)', 1:M)
X = b(idx)
That's exactly what im2col (from the Image Processing Toolbox) does:
b = (1:20).'; %'// example data
L = 18; % // desired length of sliding blocks
x = im2col(b, [L 1]); % // result
I'd use horzcat. For example:
function X = reshaper(b,len)
diff = length(b) - len + 1;
X = b(1:len);
for i=2:diff
X = horzcat(X,b(i:len+(i-1)));
end
You could probably remove the for loop with some further thought.

How to cut a matrix in Matlab?

I would like to transform the matrix A into the matrix B without using cells (e.g. mat2cell) in Matlab, where
A=[1 2 3;
4 5 6;
7 8 9;
10 11 12;
13 14 15;
16 17 18;
19 20 21;
22 23 24;
25 26 27];
B=[1 2 3 10 11 12 19 20 21;
4 5 6 13 14 15 22 23 24;
7 8 9 16 17 18 25 26 27];
All you need is some reshape + permute magic -
N = 3; %// Cut after every N rows and this looks like the no. of columns in A
B = reshape(permute(reshape(A,N,size(A,1)/N,[]),[1 3 2]),N,[])
This builds a linear index to rearrange the entries of A and then reshapes into the desired matrix B:
m = 3; %// cut size in rows of A. Assumed to divide size(A,1)
n = size(A,2);
p = size(A,1);
ind = bsxfun(#plus, ...
bsxfun(#plus, (1:m).', (0:n-1)*p), permute((0:p/m-1)*m, [1 3 2]));
B = reshape(A(ind(:)), m, [])

subset matrix using find function in Matlab

I made a matrix in Matlab, say,
A = magic(5);
A =
17 24 1 8 15
23 5 7 14 16
4 6 13 20 22
10 12 19 21 3
11 18 25 2 9
Now I found the indices I want using the find function as:
ind = find(A(:,5)>3 & A(:,4)>= 8);
ind =
1
2
3
Now if I want to get a subset of matrix A for those indices using B = A(ind) function, I only get the first column of the matrix:
B = A(ind)
B =
17
23
4
How can I get all the columns as subset??
Oops ... I got it
B = A(ind,:);