How to obtain the x and y vectors for those whose state is set to i in MATLAB - matlab

I have a MATLAB function called nearInfectious2, which represents six people. It takes in parameters of an array of x coordinates, an array of y coordinates, different states relating to those coordinates "s" for susceptible, "i" for infectious and "r" for recovered and lastly a radius. I want to store an array or array(s) of the x and y coordinates for those people whose state is "i". This is what I have thus far, but it is also including the other coordinates which is not what I want. How can I get it to just return arrays with coordinates relating to those whose state is "i"? This is what I have thus far:
[x,y]=nearInfectious2([3,350,150,20,204,103],[92,9,200,5,350,34],["s","i","s","r","i","i"],20);
function [x,y] = nearInfectious2(x,y,states,radius)
for j=1:6
if states(j) == "i"
x(j)=x(j);
y(j)=y(j);
end
end
disp([x])
disp([y])
end
After I have the array of coordinates relating to the people who's states are "i", I want to calculate the distance (using the normal distance formula sqrt((x2 - x1)^2 + (y2 - y1)^2)) between any individuals whose state is "s", to those whose state is "i" to see if it less than radius apart. Any help is greatly appreciated.

Well, your problem is that your input variables are the same as output and also x(j)=x(j) means nothing. You are assigning a value of an array to the same place in the same array.
You should be creating a new array with only the values that match. A quick fix to your code could look like this:
function [x_out, y_out] = nearInfectious2(x_in, y_in, states, radius)
x_out = []
y_out = []
for j=1:length(states)
if states(j) == "i"
x_out(end + 1) = x_in(j); % append after the end of the array
y_out(end + 1) = y_in(j);
end
end
disp([x_out])
disp([y_out])
end

Related

MATLAB - Create Array Variable in For Loop and Plot

I will preface this post with the obvious fact that I'm not very experienced in MATLAB and this post may be somewhat confusing. Any help is appreciated!
I need to store data inside two parameters but unsure on how to do it. The number of "x" values is known but it is a user inputted value, so it's not something that can be hard coded. Same as the "y" values. Here's a simplified example of what I think I need (numbers are hard coded here for the sake of the example).
Then, the final figure should have multiple plots on it. Each "x" variable is its own "output" that needs to be plotted. In the end I need "x" number of plots with "z" and "y" being the (X,Y) coordinates for each "x" plot, respectively.
EDIT: Updated example code.
list = [.0025, .005, .0075];
x = input('How many? ');
y = linspace(2.4*10^9, 5.0*10^9, 1000);
z = zeros(x, length(y));
for i = x
time = list(i)/(3*10^8);
for j = y
z(i,j) = (time * j);
end
end
for i = x
plot(z(i,j));
end
I get the following error:
Requested 3x2400000000 (53.6GB) array exceeds maximum array size preference. Creation of arrays greater
than this limit may take a long time and cause MATLAB to become unresponsive. See array size limit or
preference panel for more information.
The example that I provided could be totally wrong but I hope I have explained enough for someone to provide feedback.
Create the z-Array beforehand to your needs: https://uk.mathworks.com/help/matlab/ref/zeros.html
Then you can fill it with z[x,y] = x+y
HTH

Improving performance of sliding window summation of a matrix

This is a follow-up question to this post:
Sliding window summation of a matrix
I have a 50x50 matrix, and I'd like to sum up the values in every 10x10 (or another set size value - always square) overlapping grid i.e.:
Overlapping windows are shown only in the diagonal for the sake of clarity. I'd then like to define a particular coordinate (index) in the matrix e.g. m(23,3) and get a list of all windows this coordinate is contained within.
My attempt to solve this problem is as follows:
x = rand(50,50);
result = conv2(x, ones(10), 'valid'); %calculate the sum for each overlapping 10 x 10 window
tmp = zeros(50,50);
tmp(23,3) = 1; %location of a person, in this case - 23,3
result2 = conv2(tmp, ones(10), 'valid');
xx = find(result2); %get all non-zero values i.e. the windows the person falls into
I then also want to detect situations where any window the person is a part of, also has a sum above a certain value e.g.:
if sum(result(xx)>55)>0
#do something
end
It works - but I need to call this as part of a function, and it has to run a huge number of times. It is not the most efficient method of doing this i'm sure - and it is currently adding unacceptable levels of runtime to my script.
Can anybody show me the most efficient way of doing this? Perhaps the windows the person falls into does not have to be explicitly detected but can be calculated mathematically? At the end of the day, all I want to know is if a given person at a specific coordinate is part of a window that also contains a value above X.
We start with:
w = 10; % size of square window
x = rand(50,50); % original image
result = conv2(x, ones(w), 'valid');
% sum for each overlapping w x w window
Note that result(1,1) contains the sum for the window x(1:w,1:w).
Given the coordinates p=[23,3] in the original image x, the set of windows that contain this coordinate are the ones at result(p(1)-(0:9),p(2)-(0:9)), but excluding the out-of-bound indices. Excluding these is as follows:
i = p(1)-(0:9);
j = p(2)-(0:9);
i(i<1 | i>size(result,1)) = [];
j(j<1 | j>size(result,2)) = [];
If you want to see if any of those windows is above a certain value, do
if any(result(i,j)>55)
% do something
end

Interpn - changing output

I have 4 grids:
kgrid which is [77x1]
x which is [15x1]
z which is [9x1]
s which is [2x1]
Then I have a function:
kprime which is [77x15x9x2]
I want to interpolate kprime at some points ksim (750 x 1) and zsim (750 x 1) (xsim is a scalar). I am doing:
[ks, xs, zs, ss] = ndgrid(kgrid, x, z, [1;2]);
Output = interpn(ks, xs, zs, ss, kprime, ksim, xsim, zsim, 1,'linear');
The problem with this interpolation is that the output given is for all combinations of ksim and zsim, meaning that the output is 750x750. I actually need an output of 750x1, meaning that instead of interpolation at all combinations of ksim and zsim I only need to interpolate at ksim(1,1) and zsim(1,1), then ksim(2,1) and zsim(2,1), then ksim(3,1) and zsim(3,1), etc.
In other words, after getting Output I am doing:
Output = diag(squeeze(Output));
I know I can use this output and then just pick the numbers I want, but this is extremely inefficient as it actually interpolates on all other points which I actually do not need. Any help appreciated.
tl;dr: Change xsim and (ssim) from scalars to vectors of the same size as ksim and zsim
Output = interpn (ks, xs, zs, ss, ...
kprime, ...
ksim, ...
repmat(xsim, size(ksim)), ... % <-- here
zsim, ...
repmat(1, size(ksim)), ... % <-- and here
'linear');
Explanation:
The ksim, xsim, zsim, and ssim inputs all need to have the same shape, so that at each common position in that shape, each input acts as an "interpolated subscript" component to the interpolated object. Note that while they all need to have the same shape, this shape can be arbitrary in terms of size and dimensions.
Conversely, if you pass vectors of different sizes (after all, a scalar is a vector of length 1), these get interpreted as the components of an ndgrid construction instead. So you were actually telling interpn to evaluate all interpolations on a grid defined by the vectors ksim, and zsim (and your singletons xsim and ssim). Which is why you got a 2D-grid-looking output.
Note that the same scheme applies with the constructing vectors as well (i.e. ks, xs, zs and ss) i.e. you could have used "vector syntax" instead of "common shape" syntax to define the grid instead, i.e.
Output = interpn(kgrid, x, z, s, kprime, % ...etc etc
and you would have gotten the same result.
From the documents:
Query points, specified as a real scalars, vectors, or arrays.
If Xq1,Xq2,...,Xqn are scalars, then they are the coordinates of a single query point in Rn.
If Xq1,Xq2,...,Xqn are vectors of different orientations, then Xq1,Xq2,...,Xqn are treated as grid vectors in Rn.
If Xq1,Xq2,...,Xqn are vectors of the same size and orientation, then Xq1,Xq2,...,Xqn are treated as scattered points in Rn.
If Xq1,Xq2,...,Xqn are arrays of the same size, then they represent either a full grid of query points (in ndgrid format) or scattered points in Rn.
Answer
You want the usage highlighted in bold. As such, you have to make sure that xsim and ssim ('1' in your code sample) are of size 750x1 also. Then all the query vectors are same length and orientation, such that it can be recognized as a vector of scattered points in Rn. The output will then be a 750x1 vector as needed.
This is to elaborate on #tvo/#Tasos answers, to test the fastest way to create a vector from a scalar:
function create_vector(n)
x = 5;
repm_time = timeit(#()repm(x,n))
repe_time = timeit(#()repe(x,n))
vrep_time = timeit(#()vrep(x,n))
onesv_time = timeit(#()onesv(x,n))
end
function A = repm(x,n)
for k = 1:10000
A = repmat(x,[n 1]);
end
end
function A = repe(x,n)
for k = 1:10000
A = repelem(x,n).';
end
end
function A = vrep(x,n)
v = ones(n,1);
for k = 1:10000
A = x*v;
end
end
function A = onesv(x,n)
for k = 1:10000
A = x*ones(n,1);
end
end
And the results are (for n = 750):
repm_time =
0.049847
repe_time =
0.044188
vrep_time =
0.0041342
onesv_time =
0.0024869
which means that the fastest way to create a vector from a scalar is simply writing x*ones(n,1).

Random sample of points from a rectangular box with a spherical obstacle

The workspace is given as:
limits=[-1 4; -1 4; -1 4];
And in this workspace, there is a spherical obstacle which is defined as:
obstacle.origin_x=1.6;
obstacle.origin_y=0.8;
obstacle.origin_z=0.2;
obstacle.radius_obs=0.2;
save('obstacle.mat', 'obstacle');
I would like to create random point in the area of lim. I created random points using the code below:
function a=rndmpnt(lim, numofpoints)
x=lim(1,1)+(lim(1,2)-lim(1,1))*rand(1,numofpoint);
y=lim(2,1)+(lim(2,2)-lim(2,1))*rand(1,numofpoint);
z=lim(3,1)+(lim(3,2)-lim(3,1))*rand(1,numofpoint);
a=[x y z];
Now I would like to eliminate the points in the area of limits-obstacle. how can I do that?
You want to reject the points within the obstacle. Naturally, after rejection you will probably end up with fewer points than numofpoint. So the process will need to be repeated until enough points are generated. A while loop is appropriate here.
Rejection is done by finding ix (indices of acceptable points) and appending only those points to matrix a. The loop repeats until there are enough of those, and returns exactly the number requested.
function a = rndmpnt(lim, numofpoints)
a = zeros(3,0); % begin with empty matrix
while size(a,2) < numofpoint % not enough points yet
x=lim(1,1)+(lim(1,2)-lim(1,1))*rand(1,numofpoint);
y=lim(2,1)+(lim(2,2)-lim(2,1))*rand(1,numofpoint);
z=lim(3,1)+(lim(3,2)-lim(3,1))*rand(1,numofpoint);
ix = (x - obstacle.origin_x).^2 + (y - obstacle.origin_y).^2 + (z - obstacle.origin_z).^2 > obstacle.radius_obs^2;
a = [a, [x(ix); y(ix); z(ix)]];
end
a = a(:, 1:numofpoint);
end
You may want to add a safeguard against infinite loop (some limit on the number of cycles) in case the user passes in the values such that there are no acceptable points.

Using Negative Values in Matlab

In order to find best fit (thru polyfit), i am getting negative p value but matlab is not accepting it (Subscript indices must either be real positive integers or logicals.). Is there any way that I can use it ? I can't think of alternative method. I'll always get negative values.
EDIT:
I am trying to flattening baseline of a curve, for that. I am running for loop to have fit from 1 to 3 order. And then I am using smallest normr s value to to find the best fit and then subtract it from the whole curve to get baseline straight. I tried with few curves it works well but not with all of the data because of above describes issue.
part of the code I am working on:
for i=1:3
[p,s]=polyfit(x,y,i);
a=s.normr;
b(i,1)=p(1);
normr(i,1)=a;
ind=find(b==min(b));
mn=b(ind,1);
Yflat=y-mn(1)*(x-mean(x));
ca{2,2}=Yflat;
clear a b normr p s rte ind ind2 Yflat
end
When I translate an image into negative coordinates,
I usually record an offset e.g.
offset = [ -5, -8.5 ]
and save the intensity values in matrix begin with (1, 1) as usual,
But when comes to calculation, let the coordinates array add up with the offset
e.g. [ actualX, actualY ] = [ x, y ] + offset ;
It may need extra efforts, but it works.
Good Luck!
The code below (your code from the comments + initialization of x, y) executes. What is the problem?
x = 1:50;
y = randn(size(x));
for i=1:3
[p,s]=polyfit(x,y,i);
a=s.normr;
b(i,1)=p(1);
normr(i,1)=a;
ind=find(b==min(b));
mn=b(ind,1);
Yflat=y-mn(1)*(x-mean(x));
ca{2,2}=Yflat;
end