How to get a certain output in Matlab without for loops? - matlab

I have a list of vertices where each row corresponds to a (x, y) coordinate.
For example, the following includes the vertices (0, 0), (1, 0), and (0, 1)
V = [0 0;
1 0;
0 1];
I also have a list of edges where the first column specifies the row of the starting vertex and the second column specifies the row of the ending vertex.
For example, the following includes the edges (0, 0) to (1, 0) and (0, 0) to (0, 1)
E = [1 2; % V(1) -> V(2) = (0, 0) -> (1, 0)
1 3] % V(1) -> V(3) = (0, 0) -> (0, 1)
I need to produce a list of edges with their actual coordinates from these two lists. That is, from V and E, I need
edge1 = [0 0]; % = E(1, 1) = V(1)
edge2 = [0 0]; % = E(2, 1) = V(1)
edge3 = [1 0]; % = E(1, 2) = V(2)
edge4 = [0 1]; % = E(2, 2) = V(3)
I know how to do this with for loops, but my supervisor said there is a more optimal solution using the function find(x), which returns the nonzero indices in an array. I do not see how this could be done with find. Is there a way that this could be done without using for loops but using the find function?

As far as I remember find is not really recommended when it comes to runtime, and for loops are not as bad as they where in the past taking JIT into consideration and MATLAB's effort to improve them.
However one possible solution without using for-loops (and without find) would be:
E = [1 2;
1 3];
V = [0 0;
1 0;
0 1];
Edges=V(E(:),:)
Edges =
0 0
0 0
1 0
0 1
So the output is not a list of different variables / edges but rather a matrix holding all of them row-wise.

Related

Multidimensional data storage and interpolation

I have a function (so to speak, i actually have data with this characteristic) with one variable x and several parameters a, b and c, so y = f(x, a, b, c).
Now i want to interpolate within families of parameters (for example for variations of a).
I'm currently doing this for data with one parameter (here, y is the data matrix)
% generate variable and data
x = linspace(0, 1, 100);
a = [0, 1]; % parameter
for i = 1:length(a)
y(:, i) = x.^2 + a(i);
end
% interpolate:
yi = interp1(a, y.', 0.5);
This works fine, but how do i expand this to more dimensions?
My current data format is like this: Each column of my data matrix represents one specific set of parameters, so for example:
0 0 0 0
1 1 1 1
2 2 2 2
3 3 3 3
where the first column denotes a = 0, b = 0, the second a = 1, b = 0, the third a = 0, b = 1 and the last a = 1, b = 1 (values are just for clarification, this is not on purpose binary. Also, the data columns are obviously not the same).
This data format is just the consequence of my data aquisition scheme, but i'm happy to change this into something more useful. Whatever works.
Works well for me:
% generate variable and data
x = linspace(0, 1, 100);
a = [0, 1, 2]; % parameter
b = [3, 4, 5]; % parameter
c = [6, 7, 8]; % parameter
% Create grid
[X,A,B,C]=ndgrid(x,a,b,c);
% define function
foo = #(x,p1,p2,p3) p1.*x.^2 + p2.*x + p3;
% evaluate function
Y = foo(X,A,B,C);
% interpolate:
yi = interpn(X,A,B,C,Y,x,1,4,6);
#zlon's answer works fine for the interpolation part, here i want to show how to convert the data from the format i provided to the needed format for the interpolation.
The two-dimensional matrix must be transformed into a N-dimensional one. Since the columns are not necessarily in order, we need to find the right ones. This is what i did:
First, we need to know the parameter set of each column:
a = [ 2, 2, 1, 0, 0, 1 ];
b = [ 1, 0, 0, 1, 0, 1 ];
These vectors length match the number of columns in the data matrix. The first column for example now contains the data for a = 2 and b = 1.
Now we can generate the new table:
A = -Inf;
i = 1;
while true
A = min(a(a > A)); % find next a
if isempty(A)
break
end
idxa = find(a == A); % store possible indices
B = -Inf;
j = 1;
while true
B = min(b(b > B))); % find next b
if isempty(B)
break
end
idxb = find(b == B); % store possible indices
% combine both indices
idx = intersect(idxa, idxb);
% save column in new data table
data(:, i, j) = olddata(:, idx);
% advance
j = j + 1;
end
i = i + 1;
end

How to loop through 1D array elements multiple times?

Assuming size_of_array < n, to iterate through array elements multiple times, normally I would use something like (in C++):
for (size_t i = 0; i < n; ++i)
{
elem = arr[ i % size_of_arr ];
}
If arr = {1, 2, 3};, for elem I would get:
1 2 3 1 2 3 ...
However, in MATLAB / Octave indexes start from 1 and:
for i = 1 : n
elem = arr( mod( i, length(arr) + 1) );
end
so when n == length(arr) I get an error:
error: arr(0): subscripts must be either integers 1 to (2^31)-1 or logicals
How is this done when indexes start from 1?
In c++ you would do
arr[ i % size_of_arr]
where the inner index i % size_of_arr is in range [0 size_of_arr-1]
In MATLAB, i is in range [1 size_of_arr], thus just change to
mod( i-1, numel(arr) ) + 1
the first bit (mod( i-1, numel(arr))) makes sure that the index is inside [0 size_of_arr-1], and the you just need to add 1 to that.

fill gaps specified as "0" in 2D matrix not taking into account NaN

I'm working with a 2D matrix (global soiltype grid) that I want to fill up to fit a new mask. For that, my idea is to fill the blanks using an interpolation by nearest neighbor. I don't see how can I apply that interpolation just to the zeros, and not considering NaNs (which represent the sea). I was thinking about filling those blanks by hand as there aren't too many of them, but I thought that it's interesting to know how to do this anyway. I'd like grid cells representing islands in the middle of the ocean to consider the closest coast as nearest neighbor, if that makes sense. I know that's not realistic, but for my purposes is good enough.
Thank you in advance for any ideas. I don't play with Matlab very often and this kind of things are too much of a challenge timewise.
I advice you to use the function isnan() of matlab.
Here's un example:
A = [1 0 4, 0 3 NaN, NaN 4 5, 0 0 0, NaN 1 NaN]
A =
1 0 4
0 3 NaN
NaN 4 5
0 0 0
NaN 1 NaN
By using isnan(A) will return you a matrix with 1's where there are NaN's and 0 elsewhere.
isnan(A)
ans =
0 0 0
0 0 1
1 0 0
0 0 0
1 0 1
Then you can use the returned matrix (same size as A) as a mask for something else and/or replace the NaN's with whatever you want.
Hope this helps!
This is what I came up with.
function result = nonNanNearestNeighbor(A)
[gridX, gridY] = meshgrid(1:size(A,2), 1:size(A,1));
%if you don't want periodic BCs change function below
t = PeriodicBC(gridY - 1, size(A,1));
b = PeriodicBC(gridY + 1, size(A,1));
l = PeriodicBC(gridX - 1, size(A,2));
r = PeriodicBC(gridX + 1, size(A,2));
%Convert from rc notation to index notation
T = sub2ind(size(A), t, gridX);
B = sub2ind(size(A), b, gridX);
L = sub2ind(size(A), gridY, l);
R = sub2ind(size(A), gridY, r);
%Shift the stencils until they're not nans
while any(isnan(A(T(:))))
[tNaN, gX] = ind2sub(size(A), T(isnan(A(T))));
T(isnan(A(T))) = sub2ind(size(A), PeriodicBC(tNaN - 1, size(A,1)), gX);
end
while any(isnan(A(B(:))))
[bNaN, gX] = ind2sub(size(A), B(isnan(A(B))));
B(isnan(A(B))) = sub2ind(size(A), PeriodicBC(bNaN + 1, size(A,1)), gX);
end
while any(isnan(A(L(:))))
[gY, lNaN] = ind2sub(size(A), L(isnan(A(L))));
L(isnan(A(L))) = sub2ind(size(A), gY, PeriodicBC(lNaN - 1, size(A,2)));
end
while any(isnan(A(R(:))))
[gY, rNaN] = ind2sub(size(A), R(isnan(A(R))));
R(isnan(A(R))) = sub2ind(size(A), gY, PeriodicBC(rNaN + 1, size(A,2)));
end
result = (A(T) + A(B) + A(L) + A(R)) / 4;
end
function shifted = PeriodicBC(shifted, value)
shifted(shifted <= 0) = value - shifted(shifted <= 0);
shifted(shifted > value) = shifted(shifted > value) - value;
if any((shifted(:) <= 0) | (shifted(:) > value))
shifted = PeriodicBC(shifted, value);
end
end

Matlab Combine Row Vectors into one row

Is there a vectorized, automated way to fill a row vector l times with repeating numbers x such that x is increased by y after a certain number k of elements? k, l, x, and y are given.
Two examples:
(k = 4, l = 4, x = 0, y = 1): $A = [0 0 0 0; 1 1 1 1; 2 2 2 2; 3 3 3 3];$
(k = 2, l = 3, x = 0, y = 0.1): $B = [0 0; 0.1 0.1; 0.2 0.2]$
You can use repmat together with a:b
This way your fist example would look like this:
repmat((0:3)', 1,4)
The second one:
repmat((0:0.1:0.2)', 1,2)
You can also try linspace or similar functions to be as close to what you want as possible

Matlab: Applying threshold to one dimension in a matrix

I have a matrix M(x,y). I want to apply a threshold in all values in x, such that if x
Example:
M = 1, 2;
3, 4;
5, 6;
If t = 5 is applied on the 1st dimension, the result will be
R = 0, 2;
0, 4;
5, 6;
One way (use M(:,1) to select the first column; M(:,1)<5 returns row indices for items in the first column that are lest than 5))-
> R = M;
> R(M(:,1)<5,1) = 0
R =
0 2
0 4
5 6
Another -
R = M;
[i,j]=find(M(:,1)<5); % locate rows (i) and cols (j) where M(:,1) < 5
% so j is just going to be all 1
% and i has corresponding rows
R(i,1)=0;
To do it in a matrix of arbitrary dimensions:
thresh_min = 5;
M(M < thresh_min) = 0;
The statement M < thresh_min returns indices of M that are less than thresh_min. Then, reindexing into M with these indices, you can set all of these valuse fitting your desired criterion to 0 (or whatever else).