MATLAB: indexing a 3d matrix with another 3d matrix - matlab

Say I have two 3D matrices of same dimension (3x3x2):
A =
ans(:,:,1) =
0 0 0
0 0 0
0 0 0
ans(:,:,2) =
0 0 0
0 0 0
0 0 0
B =
ans(:,:,1) =
0 1 0
0 1 0
0 1 0
ans(:,:,2) =
1 0 0
1 0 0
0 0 1
I would like to index the last page (third dimension) of A where the corresponding page of B equals 1 and turn those values into 2, so that A becomes:
A =
ans(:,:,1) =
0 0 0
0 0 0
0 0 0
ans(:,:,2) =
2 0 0
2 0 0
0 0 2
How can I do it? Is there an easy way?

This could be one approach to set the elements in last page in the third dimension of A at places where the last page in the third dimension of B have 1's -
A(find(B(:,:,end)==1) + numel(B) - numel(B(:,:,1))) = 2
Sample run -
%// Starting input, A
A(:,:,1) =
0.2187 0.1097 0.4046 0.3658
0.1058 0.0636 0.4484 0.7635
A(:,:,2) =
0.6279 0.9329 0.1920 0.6963
0.7720 0.9727 0.1389 0.0938
A(:,:,3) =
0.5254 0.8611 0.3935 0.7413
0.5303 0.4849 0.6714 0.5201
%// Input, B
B(:,:,1) =
2 2 1 1
1 1 3 2
B(:,:,2) =
3 3 3 2
2 2 3 1
B(:,:,3) =
1 1 3 3
2 1 2 2
%// Output, A
A(:,:,1) =
0.2187 0.1097 0.4046 0.3658
0.1058 0.0636 0.4484 0.7635
A(:,:,2) =
0.6279 0.9329 0.1920 0.6963
0.7720 0.9727 0.1389 0.0938
A(:,:,3) =
2.0000 2.0000 0.3935 0.7413
0.5303 2.0000 0.6714 0.5201

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.

Matlab Find number of consecutive zeros

I want to find the lengths of series zeros in a matrix
A = [0 0 0 3 1 4 6 0 9 1 0 0 0 0 0 0 1 5 2 1 1;2 3 1 0 0 4 6 0 0 0 2 3 8 6 0 0 0 0 0 1 1]
I need result gives seriesZeros = [3 1 6;2 3 5] and also [rows,cols] from series of zeros value
thank you very much...
You could do this as follows:
A = [0 0 0 3 1 4 6 0 9 1 0 0 0 0 0 0 1 5 2 1 1;
2 3 1 0 0 4 6 0 0 0 2 3 8 6 0 0 0 0 0 1 1];
[N,~] = size(A);
% pad A==0 with zeros, and calculate diff for each row
A2 = diff([zeros(N,1) A==0 zeros(N,1)],[],2);
out_mtx = [];
for row_i = 1:size(A2,1)
row = A2(row_i, :);
zero_lengths = find(row == -1) - find(row == 1);
out_mtx(end+1,:) = zero_lengths;
end
out_mtx
Which gives
out_mtx =
3 1 6
2 3 5

what do the commas in (cl,:,k) in MATLAB do, when building a matrix

I know how to build a matrix within MATLAB but the example I am working on has is defined as
a(cl,:,k)=x*ang;
cl, k, x and ang are already defined. I just wondered what the (cl,:,k) does, in particular the role of the commas?
Also, if I were to replicate this within Excel then how would I do so?
The comma , in a(cl,:,k) is to separate different dimensions of the matrix a.
The colon : in a(cl,:,k) is to select all elements along this dimension (restricted by other dimensions), which is shorthand notation for 1:end. In other words, all elements a(cl, 1:end, k) are selected, where end is the size of the second dimension of a.
For example:
a = zeros(2, 3); // 2x3 matrix with all elements are 0
a(1, :) = [1 2 3]; // <=> a(1,1:3)=[1 2 3]; assign all elements to the first row
then, a will be
1 2 3
0 0 0
The commas separate the indices along different axes of the elements of the multi dimensional array you want to access
: means 1:end - here end will become the largest index possible along that axis
>> a = zeros(3,3,3)
a(:,:,1) =
0 0 0
0 0 0
0 0 0
a(:,:,2) =
0 0 0
0 0 0
0 0 0
a(:,:,3) =
0 0 0
0 0 0
0 0 0
>> a(1,:,1) = 1
a(:,:,1) =
1 1 1
0 0 0
0 0 0
a(:,:,2) =
0 0 0
0 0 0
0 0 0
a(:,:,3) =
0 0 0
0 0 0
0 0 0
>> a(2,1:end,2) = 2
a(:,:,1) =
1 1 1
0 0 0
0 0 0
a(:,:,2) =
0 0 0
2 2 2
0 0 0
a(:,:,3) =
0 0 0
0 0 0
0 0 0
>> a(1,1,:) = 5
a(:,:,1) =
5 1 1
0 0 0
0 0 0
a(:,:,2) =
5 0 0
2 2 2
0 0 0
a(:,:,3) =
5 0 0
0 0 0
0 0 0

How to vectorize matrix subsetting such that find() returns a matrix?

Given a matrix A, I need to find the indices corresponding to the values 1 and 2. I could do this sequentially as follows:
>> B
B =
1 2 3
4 1 6
7 8 9
4 5 1
>> find(B==1)
ans =
1
6
12
>> find(B==2)
ans =
5
But if I do this kind of operation in a loop, Matlab will only use one core of my processor. How can I vectorise it, so that I obtain a matrix from find? I want this result:
>> my_find( B, [1 2] )
ans =
1 5
6 0
12 0
(or some other padding)
How can I obtain this?
Just don't use find
B==1
ans =
1 0 0
0 1 0
0 0 0
0 0 1
B==2
ans =
0 1 0
0 0 0
0 0 0
0 0 0
And then add or logical OR those together.
i.e.
(B==1) + (B==2)
ans =
1 1 0
0 1 0
0 0 0
0 0 1
or
(B==1) | (B==2)
ans =
1 1 0
0 1 0
0 0 0
0 0 1
[i, j] = ind2sub(size(B), find(logical(sum(bsxfun(#eq, B(:), [1 2]), 2))))

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.