Apply 2D threshold to 3D array - matlab

With a geographical grid the size 20x30, I have two (temperature) variables:
The data A with the size 20x30x100
and a threshold the size 20x30
I'd like to apply the threshold to the data, i.e. to cut out the values in A that are above threshold, with every grid point having its own threshold. Since that will give a different number of values for each grid point, I thought to pad the rest with zeros, so that the resulting variable, let's call it B, will also be of the size 20x30x100.
I was thinking to do something like this, but there's something wrong with the loop:
B = sort(A,3); %// sort third dimension in ascending order
threshold_3d = repmat(threshold,1,1,100); %// make threshold into same size as B
for i=1:20
for j=1:30
if B(i,j,:) > threshold_3d(i,j,:); %// if B is above threshold
B(i,j,:); %// keep values
else
B(i,j,:) = 0; %// otherwise set to zero
end
end
end
What is the correct way to do the loop?
What other options to do this are there?
Thanks for any help!

You can use bsxfun for a much more efficient solution which will internally take care of the replication being done with repmat, like so -
B = bsxfun(#times,B,bsxfun(#gt,B,threshold))
A more efficient solution might be to use logical indexing to set the False elements from the mask created by bsxfun(gt, i.e. True ones with the use of bsxfun(#le in B to zeros thereby avoiding the use of bsxfun(#times, which for huge multidimensional arrays could be a bit expensive, like so -
B(bsxfun(#le,B,threshold)) = 0
Note on efficiency : Being a relational operation, going with the vectorized operation with bsxfun would provide both memory and runtime efficiency. The memory efficiency part has been discussed here - BSXFUN on memory efficiency with relational operations and the performance numbers have been researched here - Comparing BSXFUN and REPMAT.
Sample run -
>> B
B(:,:,1) =
8 3 9
2 8 3
B(:,:,2) =
4 1 8
4 5 6
B(:,:,3) =
4 8 5
5 6 5
>> threshold
threshold =
1 3 9
1 9 1
>> B(bsxfun(#le,B,threshold)) = 0
B(:,:,1) =
8 0 0
2 0 3
B(:,:,2) =
4 0 0
4 0 6
B(:,:,3) =
4 8 0
5 0 5

Related

How do I make a matrix with uniformly increasing elements without loop

0 0 1 1
1 1 2 2
2 2 3 3
3 3 4 4
4 4 5 5
I want to make matrix like above without for loops.
I only know how to do it with a loop.
This is my code
x = [0 0 1 1];
for i = 1:4
x= [x;x(1,:)+i]
end
Is there a way in a vector like function ':'? Or in other ways.
I want to know how to insert an increased element value into a matrix row without loop.
You could use bsxfun:
result = bsxfun(#plus,x,(0:4).')
In Matlab 2016b or newer you can also directly expand singleton dimensions:
result = x + (0:4).'
You can also use cumsum to cumulatively sum down the columns. So create your starting vector, with a matrix of ones underneath for the other rows.
cumsum([0 0 1 1; ones(4,4)]) % ones(n-1, 4) for result with n rows, input 4 columns
This has the advantage of being able to do other step sizes easily
cumsum([0 0 1 1; 2*ones(4,4)]) % steps of 2
Furthermore, it can handle different intervals in each column if we employ repmat
% Row one ↓ interval per col ↓
cumsum([0 0 1 1; repmat([1 2 3 4], 4, 1)]); % Again, use n-1 in place of 4
If you vertically concatenate the row vectors you want and then take the transpose you will get the required result (ie x=[0:4;0:4;1:5;1:5]' in this example).
You can use kron + one of methods suggested here.
kron(hankel(0:4,4:5),[1 1])

how to work with diagonals of a matrix in matlab without using loops?

Lets say I have a matrix and I want to change it's elements.
How can I tell matlab to go the element in the (i,j) place? for
example:
matrix(i;j) ---> if i=j (then do something for example matrix(i;j)^2
else ( do something else)
matrix=rand(n)
if (i=j)
matrix(i,j)=(matrix(i.j))^
else matrix(i,j)=matrix(i,j)*3
end
now how can I reach the matrix's diagonals? I am new to matlab and I didn't find an answer yet :\
I am looking for a hint or something but I can't use loops!
hey i got the answer but lets say i also want the opposite diagonal to be in square how can i do this ?? plz any help
> if a=1 2 3
> 4 5 6
> 7 8 9 i want a at the end to be a= 1 6 9
> 12 25 18
> 49 24 81
It is obvious that you do not know the basics of MATLAB. Here I will try to explain what you need a little bit, but I encourage you to read the basic documentations of programming MATLAB.
If your matrix is called A then each element can be accessed through the indices (number of row and column) of that element, for instance A(2,3) gives you the element on the second row and third column.
Also diag(A) gives all the diagonal elements of A as a vector.
What you have mentioned can be done in different ways, some of which are more efficient than the others but might be more difficult for a beginner. One of the simplest methods is using two for-loops. The first loop moves on the rows of the matrix while the second moves on the column. The goal is to check all the elements one by one.
Here, we use two variables ii and jj to count for matrix rows and columns.
ii moves on the rows, so it has to count from 1 to the number of rows your matrix has. The same is done for columns using variable jj. Moreover, to find the number of rows and columns of the matrix we use function size.
Inside the two for-loops we check if the element is a on the main diagonal with ii==jj and if its not then you do something else to that elements.
for ii=1:size(A,1)
for jj=1:size(A,2)
if ii==jj
A(ii,jj) = A(ii,jj)^2;
else
A(ii,jj) = A(ii,jj)*3;
end
end
end
A way more efficient solution is as follows:
diag(diag(A)).^2 + (tril(A,-1)+triu(A,1))*3
The firs part calculates a diagonal matrix with the elements on the main diagonal of A. The second part gets the upper and lower triangular parts of matrix.
Example:
A = [1 2 3 4;
1 2 3 4;
4 3 2 1;
4 3 2 1];
>> diag(diag(A))
ans =
1 0 0 0
0 2 0 0
0 0 2 0
0 0 0 1
>> (tril(A,-1)+triu(A,1))
ans =
0 2 3 4
1 0 3 4
4 3 0 1
4 3 2 0
Where
diag(diag(A)) + (tril(A,-1)+triu(A,1)) equals to A

Directly create sparse adjacency matrix - (matrix exceeds maxium array size preference)

Problem
I am using an adjacency matrix in my algorithm, which worked fine as long as I tested it an small matrixes (3000) points. But my actual problem includes 167620 points and I would like to create an adjacency matrix for that problem.
But obviously due to the length I get the following problem:
Requested 167620x167620 (209.3GB) array exceeds maximum array size preference. Creation of arrays greater than this limit may take a
long time and cause MATLAB to become unresponsive.
I use the following code:
adjMat = zeros(size(NB_list_all,1));
for ind = 1:size(NB_list_all,1)
adjMat(ind, nonzeros(NB_list_all(ind,2:end))) = 1;
end
adjMatS=sparse(adjMat);
G=digraph(adjMatS);
E=table2array(G.Edges);
As you can see I basically need the Edge-list.
Question
Hence the question: Is there a way to directly compute a sparse adjacency matrix or is there a different way of getting the Edge list from an adjacency list ?
My NB_list_all contains the points in column 1 and neighbours in column 2-5?
EDIT
My NB_list_all is an adajency list of the following form:
1 2 0
2 3 1
3 4 2
4 5 3
5 6 4
6 7 5
7 8 6
8 9 7
9 0 8
Column one the point indize and columns 2:5 the indizes of the points it is adjacent to. 0 if there is no neighbour. The matrix I want to create an Edge list for is 167620x5. I created the edge list before using the E=table2array(G.Edges); graph function of matlab. Now I basically have two questions:
How would i create a sparse adjacency matrix from this adjacency list directly?
Is there an easier way of creating an edge list for this kind of adjacency list ?
Thanks a lot!
If you know all the indexes and values, and the end size of the matrix, a sparse matrix can be created with
adjMat = sparse(indexi,indexj,value,size1,size2);
in a single go. Actually, this is the preffered way of creating a sparse matrix.
Example:
You want the following matrix:
0 1 1
1 0 0
0 0 1
you would build it as:
sparse([1 1 2 3], [2 3 1 3],[1 1 1 1],3,3)
with the example you gave:
NB_list_all=[
1 2 0
2 3 1
3 4 2
4 5 3
5 6 4
6 7 5
7 8 6
8 9 7
9 0 8];
% if the first index contains all numbers we can safely do this
% this is almost indexJ, but we have some zeroes that we dont like
indexJ=NB_list_all(:,2:end);
% create indexI
indexI=repmat(1:size(indexJ,1),size(indexJ,2),1).';
% lets unroll the matrices
indexI=indexI(:);
indexJ=indexJ(:);
% lets remove the indexI and NB_list_all that have a zero somewhere, because those are not real
notzero=find(indexJ);
indexI=indexI(notzero);
indexJ=indexJ(notzero);
adjMat=sparse(indexI,indexJ,1,size(NB_list_all,1),size(NB_list_all,1));

Removing third dimension of matrix

Lets say I have matrix such that A(:,:,1)=[1,2,3;2,3,4], A(:,:,2)=[3,4,5;4,5,6].
How is the easiest way of accessing and plotting the vectors (1,2,3),(2,3,4),(3,4,5),(4,5,6). I tried creating B=[A(:,:,1);A(:,:,2)], but i need a procedure to arbitrary number of A's.
Hope this isn't trivial and I've formulated myself satisfactory.
You should think 'vertically'. This will allow you to use colon indexing:
>> A(:,:,1) = [1,2,3;2,3,4].'; %'// NOTE: transpose of your original
>> A(:,:,2) = [3,4,5;4,5,6].'; %'// NOTE: transpose of your original
>> A(:,:)
ans =
1 2 3 4
2 3 4 5
3 4 5 6
The colon indexing with two colons works for any dimension A:
>> A(:,:,:,:,1,1) = [1 2 3; 2 3 4].'; %'
>> A(:,:,:,:,2,1) = [3 4 5; 4 5 6].'; %'
>> A(:,:,:,:,1,2) = [5 6 7; 6 7 8].'; %'
>> A(:,:,:,:,2,2) = [7 8 9; 8 9 0].'; %'
>> A(:,:)
ans =
1 2 3 4 5 6 7 8
2 3 4 5 6 7 8 9
3 4 5 6 7 8 9 0
Colon indexing in MATLAB is quite interesting and really powerful once you master it. For example, if you use fewer colons than there are dimensions in the array (like above), MATLAB will automatically concatenate the remainder of the data along the dimension equal to the colon count.
So, if A has 48 dimensions, but you index with just 2 colons: you'll get a 2D array, that is the concatenation of the remaining 46 dimensions along the 2nd dimension.
In general: if A has N dimensions, but you index with just M ≤ N colons: you'll get an M-D array, that is the concatenation of the remaining N-M dimensions along the Mth dimension.
So as long as you are free to define your A to contain vectors on the columns rather than the rows (you should advise everyone to do this, as virtually everything in MATLAB is a bit faster that way), I think this is the fastest and most elegant way to do what you want.
If not, well, then just reshape like Dan :)
Assuming the order does not matter, here is how you can do it for vectors of length 3:
B = reshape(shiftdim(A,2), [], 3)
plot(B')
For vectors of arbitrary dimensions, replace 3 by size(A,2)

Find "external" elements bigger than a threshold value in a 3D matrix

I was wondering if someone could help me come up with a code for a 3D image I'm working on wright now.
I've got a simple 3D matrix:
A(:,:,1) =
0 7 4
0 32 9
4 3 1
A(:,:,2) =
6 0 4
3 4 6
2 3 11
A(:,:,3) =
12 2 4
10 20 6
14 3 2
I would like to find those values that are bigger than a threshold value (for example biger than 7). However I only want those that are exterior elements, that is, not "central" elements (the 32 on the first layer of the matrix shouldn't be marked as a maximum)
(I'm working with a bigger matrix but I guess that once I'm able to do this for the small 3D matrix from above, it won't be difficult to do it for larger ones).
Thank you a lot
Try this:
A = randn(4,4,4); % data. Arbitrary size
th = 1; % threshold
ind = find(A>th);
[x y z] = ind2sub(size(A), ind);
ext = find((x==1)|(x==size(A,1))|(y==1)|(y==size(A,2))|(z==1)|(z==size(A,3)));
ind_solution = ind(ext); % linear index of desired values
solution = A(ind_solution) % desired values
I'm guessing you could extract vectors from those matrices... so it's a matter of getting the external vectors and looping trough their elements.
I think this link will help you extract a vector.