I'm new to Matlab and I want to achieve a very simple operation : I have a 792 x 1046 uint8 matrix called mg and want to convert its cells values (from 0 to 255) to values between 1 and 4 (1,2,3,4) in a new matrix called mgc accordingly to simple conditions.
Strangely, the new matrix is filled with only 1s and 2s but not any 3s or 4s...
Here is my code :
[x,y]=size(mg);
mgc = zeros(x,y);
for i=1:x
for j=1:y
if (mg(i,j)<=100)
mgc(i,j)=1;
elseif (100<mg(i,j)<=110)
mgc(i,j)=2;
elseif (110<mg(i,j)<=120)
mgc(i,j)=3;
else
mgc(i,j)=4;
end
end
end
If anyone could help me solve this stupid issue, it would be great !
THX
You shouldn't use expressions such as 100<mg(i,j)<=110 in MATLAB. Instead, use something like 100<mg(i,j) && mg(i,j)<=110.
At the moment, MATLAB is evaluating the expression 100<mg(i,j)<=110 as (100<mg(i,j))<=110. (100<mg(i,j)) is going to be either one or zero (true or false), and therefore will always be <=110. So it never gets past the second else, and your array is all either 1 or 2.
Edit: although this answer explains the specific issue you're having, you should probably instead be using logical indexing, which would be much more efficient than a double for loop (and more idiomatic in MATLAB). See the answers from #excaza or #Benoit_11 for examples of that).
As stated in the comments you need to use logical operators in your elseif statements. Just so you know, you can vectorize this whole for loop with those same logical operators as follows:
Let's define mgc2 as you did for mgc:
mgc2 = zeros(x,y);
Then you can fill mgc2 like this:
mgc2(mg<=100) =1;
mgc2(mg>100 & mg<=110) =2;
mgc2(mg>110 & mg <=120) =3;
mgc2(mg>120) =4;
You need to use and operators:
% Dummy data
mg = [10 115; 125 140];
[x,y]=size(mg);
mgc = zeros(x,y);
for i=1:x
for j=1:y
if (mg(i,j)<=100)
mgc(i,j)=1;
elseif (100 < mg(i,j) && mg(i,j) <= 110)
mgc(i,j)=2;
elseif (110 < mg(i,j) && mg(i,j) <= 120)
mgc(i,j)=3;
else
mgc(i,j)=4;
end
end
end
Returns:
mgc =
1 3
4 4
You also don't need to use a loop here, and can leverage MATLAB's logical indexing instead:
% Dummy data
mg = [10 115; 125 140];
mgc = zeros(size(mg));
mgc(mg <= 100) = 1;
mgc((mg > 100 & mg <= 110)) = 2;
mgc((mg > 110 & mg <= 120)) = 3;
mgc(mg > 120) = 4;
Which returns the same matrix.
This is because any value greater than 100 will return true for the first elseif statement.
100 < my(i,j) returns 1.
When you want to do a double condition, you must use the & operator otherwise you may have false statements
>> x = 4
>> res = 2<x<=3
res =
1
%%Using the `&` operator instead
>> res = 2<x && x<=3
res =
0
Related
As the title says, I'm looking for making a dynamic version of this one but i can't figure out how to do this. In this case r=30 and the difference between the cases is 5 (i<=5, i<=10, 1<=15 etc) Can somebody help me ?
for i = 1:r
if i <= 5
a_m_o(length(a_m_o)+1) = m_o(i) - m_o(1)
elseif i <= 10
a_m_o(length(a_m_o)+1) = m_o(i) - m_o(6)
elseif i <= 15
a_m_o(length(a_m_o)+1) = m_o(i) - m_o(11)
elseif i <= 20
a_m_o(length(a_m_o)+1) = m_o(i) - m_o(16);
elseif i <= 25
a_m_o(length(a_m_o)+1) = m_o(i) - m_o(21);
elseif i <= 30
a_m_o(length(a_m_o)+1) = m_o(i) - m_o(26);
end
if a_m_o(i)<0
a_m_o(i) = a_m_o(i) + 400;
end
end
Other answers guide you towards avoiding code duplication. But the other thing you need to do is vectorize your code. Vectorizing is a way to avoid loops, which are relatively slow in MATLAB (even though they are much faster nowadays than they used to be when I started using MATLAB).
We'll start with creating an array that indexes m_o the way you do in your loop:
I = floor(((1:r)-1)/5)*5+1;
This creates an array [1,1,1,1,1,6,6,6,6,6,11,11,...]. You can also use repmat or mod as suggested in the other answers.
Indexing m_o using I (m_o(I)) is the same as writing [m_o(1),m_o(1),m_o(1),m_o(1),m_o(1),m_o(6),m_o(6),m_o(6),...]. That is, we're indexing the same element 5 times, creating 5 copies of that element in the output array. So now you can write:
a_m_o = a_m - a_m(I);
Your test for negative results can also be vectorized:
J = a_m_o < 0;
a_m_o(J) = a_m_o(J) + 400;
Besides that vectorized code is faster, it's often also a lot easier to read!
Note also that your code a_m_o(length(a_m_o)+1) = ... is very inefficient: the array a_m_o is resized every loop iteration. Newer MATLABs actually identify this use case and optimize around it, but it's still much faster to pre-allocate the array:
a_m_o = zeros(size(m_o));
for i = 1:r
a_m_o(i) = a_m(i) - a_m(1);
end
For r=30 you might not notice the difference, but for larger arrays the savings can be huge.
Basically, you group the natural numbers (without zero) in groups of 5 (or n) elements en map each element in a group to its first element. Such an operation can typically be done using the modulus:
function k = map (i)
n = 5;
m = mod(i, n);
if m == 0
m = 5;
end
k = i - m + 1;
end
This will map 1, 2, 3, 4 and 5 to 1; 6, 7, 8, 9 and 10 to 6; ...
r_help=repmat([1:5:length(r)],[5,1]); %create a helper vector which got the indices in it which you need.
r_help=r_help(:);
for i = 1:r
a_m_o(length(a_m_o)+1) = m_o(i) - m_o(r_help(i));
if a_m_o(i)<0
a_m_o(i) = a_m_o(i) + 400;
end
end
I implemented the following user defined function in MATLAB:
function Q = Calc_Q(Head, freq)
b6 = [3.7572E-07 -1.5707E-05 6.0490E-03 5.0018E-02 2.1180E-01];
b5 = [-9.0927E-06 8.9033E-04 -3.2415E-02 5.4525E-01 -8.1649E+00] / 10e2;
b4 = [7.5172E-06 -5.6565E-04 1.0024E-02 3.5888E-01 3.8894E-02] / 10e5;
b3 = [-4.8767E-06 4.8787E-04 -1.3311E-02 -1.2189E-01 -5.3522E+00] / 10e8;
b2 = [5.9227E-06 -8.1716E-04 3.5392E-02 -4.5413E-01 1.9547E+00] / 10e11;
b1 = [-2.0004E-06 2.9027E-04 -1.3754E-02 2.3490E-01 -1.2363E+00] / 10e14;
a = [polyval(b1,abs(freq)), polyval(b2, abs(freq)), polyval(b3, abs(freq)), polyval(b4, abs(freq)), polyval(b5, abs(freq)), polyval(b6, abs(freq)) - Head];
Q_roots = roots(a);
%Delete roots with imaginary part
i = 1;
while i <= length(Q_roots)
if(imag(Q_roots(i)) ~= 0)
Q_roots(i) = [];
i = i - 1;
end
i = i + 1;
end
%Delete roots with real part greater then 3100
i = 1;
while i <= length(Q_roots)
if(Q_roots(i) >= 3100 || Q_roots(i) < 0)
Q_roots(i) = [];
i = i - 1;
end
i = i +1;
end
if freq < 0
Q = real(Q_roots(1)) * -1;
else
Q = real(Q_roots(1));
end
end
When I Call this function in Matlab it works fine. However if I use this exact code as a MATLAB function in simulink it stop's working. (actually it works, but the ouput is always zero.)
I do have a suspicion of what the problem might be. When running the script in de-bugging mode, I cannot view a result for Q_roots (It just doesn't display anything).
Q_roots = roots(a);
Any ideas ?
The problem is most likely due to your logic that eliminates any roots that do not have exactly zero in the imaginary part. This is a mathematical way of thinking that does not really work well numerically, at least not in general. All the roots are probably being found in both cases (there is no limitation that implies otherwise), but in Simulink and in code generation the problem is treated as a complex one, and some roots might be coming back with tiny imaginary parts. Instead of deleting roots if their imaginary parts are not exactly zero, eliminate the roots with imaginary parts that are numerically insignificant, either very small relative to the real part or very small altogether. Something like
tol = 10*eps(class(Q_roots));
keepers = abs(imag(Q_roots)) < tol*max(abs(real(Q_roots)),1) & ...
real(Q_roots) >= 0 & real(Q_roots) <= 3100;
Q_roots = Q_roots(keepers);
would take care of all the deletions in one fell swoop. I used 10*eps as a tolerance here.
But if you only need the first qualifying root, then you could just do this:
Q = nan('like',a);
tol = 10*eps(class(a));
for k = 1:numel(Q_roots)
r = real(Q_roots(k));
if abs(imag(Q_roots(k))) < tol*max(abs(r),1) && r >= 0 && r <= 3100;
Q = r;
break
end
end
if freq < 0
Q = -Q;
end
Oke I found the problem.
From a different forum:
Hi Cosmin,
I took a look at the implementation of roots for the Embedded MATLAB
Function block (\toolbox\eml\lib\matlab\polyfun\roots.m).
It's stated there:
% Limitations: % Output is always variable size. % Output is
always complex. % Roots may not be in the same order as MATLAB. %
Roots of poorly conditioned polynomials may not match MATLAB. The last
sentence is what makes you the headache (and yes, your polynomial is
badly conditioned!). If you take a look at the plot you will see, that
the curve hardly touches the x-axis.
I have a suggestion though: the value -z/b is a (very) good
approximation of the root you are looking for ...?
Titus
http://www.mathworks.com/matlabcentral/answers/25624-roots-in-simulink
Apparently the root function in simulink does not always found all the roots of a given polynominal.
This is unfortunate and not easily solvable. I did however found a solution.
For all the different polynomials I have to solve, I know the interval of the root I am interested in ( [-3000, 3000]).
I just basically takes steps of 50 from -3000 to 3000, until the function drops below 0. I then know the approximate solution of the root. I use this approximation as seed for the Newton-Raphson method.
Straight implementation of the Newton raphson method with a given seed for all the polynomials I have to solve did not work because sometimes it iterated to a different root (one which i was not interested in.)
Here's the code:
function Q = Calc_Q(Head, freq)
b6 = [3.7572E-07 -1.5707E-05 6.0490E-03 5.0018E-02 2.1180E-01];
b5 = [-9.0927E-06 8.9033E-04 -3.2415E-02 5.4525E-01 -8.1649E+00] / 10e2;
b4 = [7.5172E-06 -5.6565E-04 1.0024E-02 3.5888E-01 3.8894E-02] / 10e5;
b3 = [-4.8767E-06 4.8787E-04 -1.3311E-02 -1.2189E-01 -5.3522E+00] / 10e8;
b2 = [5.9227E-06 -8.1716E-04 3.5392E-02 -4.5413E-01 1.9547E+00] / 10e11;
b1 = [-2.0004E-06 2.9027E-04 -1.3754E-02 2.3490E-01 -1.2363E+00] / 10e14;
%coeff for the polynominal
a = [polyval(b1,abs(freq)), polyval(b2, abs(freq)), polyval(b3, abs(freq)), polyval(b4, abs(freq)), polyval(b5, abs(freq)), polyval(b6, abs(freq)) - Head];
%coeff for the derrivative of polynominal
da = [5*a(1) 4*a(2) 3*a(3) 2*a(4) a(5)];
Q = -3000;
%Search for point where function goes below 0
while (polyval(a, Q) > 0)
Q = Q + 25;
end
error_max = 0.01
iter_counter = 1;
while abs(polyval(a,Q)) >= error_max && iter_counter <= 1000
Q = Q - polyval(a, Q)/polyval(da, Q);
iter_counter = iter_counter + 1;
error = abs(polyval(a,Q));
end
if(freq < 0)
Q = Q * - 1;
end
end
I have a small piecewise function that profiling reveals is taking 60% of the runtime of the program. It is called very often because it goes within some integrals that I perform quite a lot in my code.
According to profiling, it is called 213560 times, taking 47.786 s in total, corresponding to ~220 microseconds per call.
I want to pass it an array, and it should return an array, operating element wise.
I know that using loops in Matlab is very slow and should be avoided, but I'm not sure how to vectorise this sort of function.
function bottleradius = aux_bottle_radius(z_array)
%AUXBOTTLERADIUS Radius of aux bottle
% This returns the radius of the aux bottle as a function of z. It works for all
% heights of aux bottle, just remember to integrate over the right height
% range
bottleradius = zeros(size(z_array));
for i = 1 : max(size(z_array))
if z_array(i)<-30e-3
%door cavity
bottleradius(i) = 34e-3;
elseif z_array(i)>=-30e-3 && z_array(i)<-20e-3
%radiussing door cavity
bottleradius(i) = 34e-3 + 10e-3 - sqrt((10e-3).^2 - (z_array(i)+30e-3).^2);
elseif z_array(i)>=-20e-3 && z_array(i)<-10e-3
%aluminium plate
bottleradius(i) = 46e-3;
elseif z_array(i)>=-10e-3 && z_array(i)<0e-3
%radiussing aluminium plate to main bottle
bottleradius(i) = 46e-3 + 10e-3 - sqrt((10e-3).^2 - (z_array(i)+10e-3).^2);
elseif z_array(i)>=0e-3
%top of Al plate, bottom of main bottle
bottleradius(i) = 185e-3;
else
bottleradius(i) = 0;
end
end
end
You can do that completely vectorized with logical operators. You can essentially replace that code with:
function bottleradius = aux_bottle_radius(z_array)
%// Declare initial output array of all zeroes
bottleradius = zeros(size(z_array));
%// Condition #1 - Check for all values < -30e-3 and set accordingly
bottleradius(z_array < -30e-3) = 34e-3;
%// Condition #2 - Check for all values >= -30e-3 and < -20e-3 and set accordingly
ind = z_array >= -30e-3 & z_array < -20e-3;
bottleradius(ind) = 34e-3 + 10e-3 - sqrt((10e-3).^2 - (z_array(ind)+30e-3).^2);
%// Condition #3 - Check for all values >= -20e-3 and < -10e-3 and set accordingly
bottleradius(z_array >=-20e-3 & z_array < -10e-3) = 46e-3;
%// Condition #4 - Check for all values >= -10e-3 and < 0 and set accordingly
ind = z_array >=-10e-3 & z_array < 0;
bottleradius(ind) = 46e-3 + 10e-3 - sqrt((10e-3).^2 - (z_array(ind)+10e-3).^2);
%// Condition #5 - Check for all values >= 0 and set accordingly
bottleradius(z_array >= 0) = 185e-3;
end
Minor comments
0e-3 doesn't make any sense precision wise. This is essentially the same as 0 and I've changed that in your code.
Note that for conditions #2 and #4, I precompute a logical array that indicates where we would need to access the corresponding values in z_array to make things easier and set those same locations in bottleradius to be the desired computed outputs. I don't do this for the other conditions because you're just setting them to a single constant.
Thankfully, you use element-wise operators for conditions #2 and #4 so there wasn't a need to change the expressions for those conditions.
I am Beginner in Matlab, i would like to plot system concentration vs time plot at a certain time interval following is the code that i have written
%Input function of 9 samples with activity and time calibrated with Well
%counter value approx : 1.856 from all 9 input values of 3 patients
function c_o = Sample_function(td,t_max,A,B)
t =(0 : 100 :5000); % time of the sample post injection in mins
c =(0 : 2275.3 :113765);
A_max= max(c); %Max value of Concentration (Peak of the curve)
if (t >=0 && t <= td)
c_o(t)=0;
else if(td <=t && t<=t_max)
c_o(t)= A_max*(t-td);
else if(t >= t_max)
c_o(t)=(A(1)*exp(-B(1)*(t-t_max)))+(A(2)*exp(-B(2)*(t- t_max)))+...
(A(3)*exp(-B(3)*(t-t_max)));
end
fprintf('plotting Data ...\n');
hold on;
figure;
plot(c_o);
xlabel('Activity of the sample Ba/ml ');
ylabel('time of the sample in minutes');
title (' Input function: Activity sample VS time ');
pause;
end
I am getting following error
Operands to the || and && operators must be convertible to logical scalar values.
Error in Sample_function (line 18)
if (t >=0 && t <= td)
Kindly .Let me know if my logic is incorrect
Your t is not a single value to compare with 0 so it cannot evaluate to true or false.
You want to do this with logical indexing
c_o = zeros(size(t));
c_o(t>=0 & t<=td) = 0; % this line is actually redundant and unnecessary since we initialized the vector to zeros
c_o(t>td & t<=t_max) = A_max*(t(t>td & t<=t_max)-td);
c_o(t>t_max) = (A(1)*exp(-B(1)*(t(t>t_max)-t_max)))+(A(2)*exp(-B(2)*(t(t>t_max)- t_max)))...
+ (A(3)*exp(-B(3)*(t(t>t_max)-t_max)));
You could also make this a little prettier (and easier to read) by assigning the logical indexes to variables:
reg1 = (t>=0 & t<=td);
reg2 = (t>td & t<=t_max);
reg3 = (t>t_max);
Then, for instance, the second assignment becomes the much more readable:
c_o(reg2) = A_max*(t(reg2)-td);
t is written as a array of numbers. So, it can't be compared with a scalar value ex. 0.
Try it in a for loop
for i=1:length(t)
if (t(i) >=0 && t(i) <= td)
c_o(t(i))=0;
else if(td <=t(i) && t(i)<=t_max)
c_o(t(i)))= A_max*(t(i)-td);
else if(t(i) >= t_max)
c_o(t)=(A(1)*exp(-B(1)*(t(i)-t_max)))+(A(2)*exp(-B(2)*(t(i)- t_max)))...
+ (A(3)*exp(-B(3)*(t(i)-t_max)));
end
end
i am doing a simulation and code for a for loops
for example
IntervalInterArrivalTime = [2 3 4 5] //user input
CDF = [250 500 750 1000] // this is the probability but multiply 1000 to alculate the range
randInterArrival = [991 351 754 823] // this is pc random generated number so each time wwill be different
lengthCDF = length(CDF); // length of the CDF which in this case is =4
period = 0;
the for loop i want do is the program will check the range of the randInterArrival with the CDF and IntervalArrivalTime. For example:
if randInterArrival >0 and <= CDF(1) , then period will be IntervalInterArrivalTime(1)
else if randInterArrival > CDF(1) and <= CDF(2) , then the period will be IntervalInterArrival(2)
else if randInterArrival > CDF(2) and <= CDF(3) , then the period will be IntervalInterArrival(3)
else if randInterArrival > CDF(3) and <= CDF(4) , then the period will be IntervalInterArrival(4)
for i=1:lengthCDF
if randInterArrival(i)>0 && randInterArrival(i)< CDF(i)
period=IntervalInterArrivalTime(i)
else if randInterArrival(i)> CDF(i) % how to continued to write the statement out so that i can loop through all the items in the CDF and check which IntervalInterArrival() it is
% i stop here and dunno how to continued to loop through the CDF and check
end
end
hope u all can help . thanks in advance
You could do this the way you're proposing, by checking each condition separately. However, this gets obnoxiously clunky as you increase the number of elements/conditions to check. A better way is to make use of the find function and rethink the logic of what you're trying to do:
What is the goal? The goal is to determine the index of the smallest CDF value that is larger than randInterArrival(i). Do this as follows:
index = find(CDF > randInterArrival(i), 1, 'first');
Voila! No if statements necessary. Use this index to get the period:
period = IntervalInterArrivalTime(index);
You need to end your statements:
for i=1:lengthCDF
if randInterArrival(i)>0 && randInterArrival(i)< CDF(i)
period=IntervalInterArrivalTime(i)
else if randInterArrival(i)> CDF(i) && ?? // i hang here ..
end
end