Trapz giving weird results - matlab

I'm having a weird result with Matlab's trapz function. I have two variables, zptest and omega, both of which are positive, 3000x2x1 arrays.
When I plot zptest vs omega (plot(zptest(:,1,1),omega(:,1,1)) the curve is clearly positive and should give a positive result when integrating. This is not the case, however, as shown below:
trapz(zptest(:,1,1),omega(:,1,1))
ans =
-0.049999940237341
just to prove that both omega and zptest are positive:
find(omega(:,1,1) < 0)
ans =
Empty matrix: 0-by-1
find(zptest(:,1,1) < 0)
ans =
Empty matrix: 0-by-1
I know I'm not giving any context to what I'm actually doing but this seems like a context independent problem. Does anyone have any idea what's going on?

Try re-ordering x in ascending order (and y-values accordingly):
x_order = x(end:-1:1); %fliplr
y_order = y(end:-1:1); %fliplr
trapz(x_order, y_order)
In trapz(x,y) differentiation of x is applied through diff(x,1,1), i.e. [x(2:n,:) - x(1:n-1,:)]. If your x is descending this will give negative dx. It doesn't matter if it is positive or negative. However, in plot the curve will appear positive-definite (you don't actually see the order of points, just pairs from two vectors on a plane).
Example (compare the following):
x = [-1 -0.5 0]; y = 0.5-x;
figure; plot(x,y); hold on; plot(-x, y,'r')
trapz(x, y)
trapz(-x, y)
figure; plot(x, y); hold on; plot(fliplr(-x), fliplr(y),'r')
trapz(fliplr(-x), fliplr(y))

Think of it like this. The integral of a function that is always positive, if the limits are inverted, will still be negative. Thus we know that
int(x^2,-1,1) = 2/3
But
int(x^2,1,-1) = -2/3
Clearly x.^2 is always a positive number, but here the limits of integration were not increasing, but decreasing.
If the x_i in the call to trapz are not in increasing order, then you will get negative results. This is reflected by trapz. Trapz sees the order of the points as presented to it.
x = -1:.1:1;
trapz(x,x.^2)
ans =
0.67
xrev = fliplr(x);
trapz(xrev,xrev.^2)
ans =
-0.67
And, as pointed out by gevang, the plot shows only that the function is positive, not the order of the points.

Related

How to inverse inline function in Matlab?

If I have anonymous function, for example:
a=3;
b=4;
y = #(x) (x)./(a+b+x);
So I easily can find a for x=4, but how can I find an x that will give me y=0.4? I actual looking for an easy way to have x(y) instead of y(x).
One trivial approach is to use a numeric algorithm to find the zero of y(x) - 0.4:
target = 0.4;
x = fzero(#(x) y(x)-target, 0)
Now, x is 4.6667 and y(x) returns 0.4.
Note that this is an easy approach, but it is not cheap computationally. Also, you need a suitable start point, which here I've set to 0. If your function has multiple points where it reaches 0.4, then you will get the one closest to this start point.
One approach is to use MATLAB's interpolation (1D) function interp1, but this works on your function for parameter values that ensure y(x) is a non-decreasing function.
step = .01; % Control precision (smaller = more precise)
Xmax = 50; % Largest x of interest
X = [0:step:Xmax]';
Y = y(X); % Generate discrete approximation of function
yinvh=#(L) interp1(Y,X,L);
Targets = [0.25 0.4 0.75]';
yinvh(Targets)
This matches the results from Cris Luengo's approach.
>> yinvh(Targets)'
ans =
2.3333 4.6667 21.0000
figure, hold on, box on
plot(X,y(X))
plot(zeros(3,1),Targets,'rx')
plot(yinvh(Targets),zeros(3,1),'rx')
for k = 1:length(Targets)
plot([0; yinvh(Targets(k))],Targets(k)*ones(2,1),'k--')
plot(yinvh(Targets(k))*ones(2,1),[0 Targets(k)],'k--')
end

Insert x-value to polyfit vector to get y-value (Matlab)

I used the Matlab command polyfit to interpolate a curve. I then wanted to calculate the y-max value of that curve. I have found the roots for the polynomial, so i now have an x-value I want to insert in the polynomial from polyfit to get the y-value for that x-value.
I have difficulties to get it working properly.
P is the polynomial, so as you know, P(1)^4, P(2)^3... and so on
P = [-1.99405270507682e+26 5.55362828633395e+24 -5.80027044841956e+22 2.69238494640005e+20 -4.68659390860982e+17]
The x-value I want to insert to get a y-value is
x = 7.765633479578490e-04
The y-value should be about 17.7.
Am I thinking right here? The x-value is correct, I have compared it to my plot.
Thanks in advance guys!
Something is wrong in your calculations. With the values you give in your question, P(x) is enormous due to the enormous polynomial coefficients you have:
P =
-1.9941e+026 5.5536e+024 -5.8003e+022 2.6924e+020 -4.6866e+017
>> x = 7.765633479578490e-04
x = 7.7656e-004
>> y = polyval(P,x)
y = -2.9203e+017
These are the roots of your polynomial, they have very small imaginary parts, but given the enormous coefficients of the polynomial, they cannot be neglected:
>> format long g
>> roots(P)
ans =
0.00696463336211033 + 9.88579484856405e-07i
0.00696463336211033 - 9.88579484856405e-07i
0.00696084682252702 + 1.06977927834625e-06i
0.00696084682252702 - 1.06977927834625e-06i

Finding values of x for a given y when approaching a limit

I'm trying to find two x values for each y value on a plot that is very similar to a Gaussian fn. The difficulty is that I need to be able to find the values of x for several values of y even when the gaussian fn is very close to zero.
I can't post an image due to being a new user, however think of a gaussian function and then the regions where it is close to zero on either side of the peak. This part where the fn is very close to reaching zero is where I need to find the x values for a given y.
What I've tried:
When the fn is discrete: I have tried interp1, however I get the error that it is not strictly monotonic increasing because of the many values that are close to zero.
When I fit a two-term gaussian:
I use fzero (fzero(function-yvalue)) however I get a lot of NaN's. These might be from me not having a close enough 'guess' value??
Does anyone have any other suggestions for me to try? Or how to improve what I've already attempted?
Thanks everyone
EDIT:
I've added a picture below. The data that I actually have is the blue line, while the fitted eqn is in red. The eqn should be accurate enough.
Again, I'm trying to pick out x values for a given y where y is very small (approaching 0).
I've tried splitting the function into left and right halves for the interpolation and fzero method.
Thanks for your responses anyway, I'll have a look at bisection.
Fitting a Gaussian seems to be uneffective, as its deviation (in the x-coordinate) from the real data is noticeable.
Since your data is already presented as a numeric vector y, the straightforward find(y<y0) seems adequate. Here is a sample code, in which the y-values are produced from a perturbed Gaussian.
x = 0:1:700;
y = 2000*exp(-((x-200)/50).^2 - sin(x/100).^2); % imitated data
plot(x,y)
y0 = 1e-2; % the y-value to look for
i = min(find(y>y0)); % first entry above y0
if i == 1
x1 = x(i);
else
x1 = x(i) - y(i)*(x(i)-x(i-1))/(y(i)-y(i-1)); % linear interpolation
end
i = max(find(y>y0)); % last entry above y0
if i == numel(y)
x2 = x(i);
else
x2 = x(i) - y(i)*(x(i)-x(i+1))/(y(i)-y(i+1)); % linear interpolation
end
fprintf('Roots: %g, %g \n', x1, x2)
Output: Roots: 18.0659, 379.306
The curve looks much like your plot.

How do I plot relations in matlab?

I want to plot relations like y^2=x^2(x+3) in MATLAB without using ezplot or doing algebra to find each branch of the function.
Does anyone know how I can do this? I usually create a linspace and then create a function over the linspace. For example
x=linspace(-pi,pi,1001);
f=sin(x);
plot(x,f)
Can I do something similar for the relation I have provided?
What you could do is use solve and allow MATLAB's symbolic solver to symbolically solve for an expression of y in terms of x. Once you do this, you can use subs to substitute values of x into the expression found from solve and plot all of these together. Bear in mind that you will need to cast the result of subs with double because you want the numerical result of the substitution. Not doing this will still leave the answer in MATLAB's symbolic format, and it is incompatible for use when you want to plot the final points on your graph.
Also, what you'll need to do is that given equations like what you have posted above, you may have to loop over each solution, substitute your values of x into each, then add them to the plot.
Something like the following. Here, you also have control over the domain as you have desired:
syms x y;
eqn = solve('y^2 == x^2*(x+3)', 'y'); %// Solve for y, as an expression of x
xval = linspace(-1, 1, 1000);
%// Spawn a blank figure and remember stuff as we throw it in
figure;
hold on;
%// For as many solutions as we have...
for idx = 1 : numel(eqn)
%// Substitute our values of x into each solution
yval = double(subs(eqn(idx), xval));
%// Plot the points
plot(xval, yval);
end
%// Add a grid
grid;
Take special care of how I used solve. I specified y because I want to solve for y, which will give me an expression in terms of x. x is our independent variable, and so this is important. I then specify a grid of x points from -1 to 1 - exactly 1000 points actually. I spawn a blank figure, then for as many solutions to the equation that we have, we determine the output y values for each solution we have given the x values that I made earlier. I then plot these on a graph of these points. Note that I used hold on to add more points with each invocation to plot. If I didn't do this, the figure would refresh itself and only remember the most recent call to plot. You want to put all of the points on here generated from all of the solution. For some neatness, I threw a grid in.
This is what I get:
Ok I was about to write my answer and I just saw that #rayryeng proposed a similar idea (Good job Ray!) but here it goes. The idea is also to use solve to get an expression for y, then convert the symbolic function to an anonymous function and then plot it. The code is general for any number of solutions you get from solve:
clear
clc
close all
syms x y
FunXY = y^2 == x^2*(x+3);
%//Use solve to solve for y.
Y = solve(FunXY,y);
%// Create anonymous functions, stored in a cell array.
NumSol = numel(Y); %// Number of solutions.
G = cell(1,NumSol);
for k = 1:NumSol
G{k} = matlabFunction(Y(k))
end
%// Plot the functions...
figure
hold on
for PlotCounter = 1:NumSol
fplot(G{PlotCounter},[-pi,pi])
end
hold off
The result is the following:
n = 1000;
[x y] = meshgrid(linspace(-3,3,n),linspace(-3,3,n));
z = nan(n,n);
z = (y .^ 2 <= x .^2 .* (x + 3) + .1);
z = z & (y .^ 2 >= x .^2 .* (x + 3) - .1);
contour(x,y,z)
It's probably not what you want, but I it's pretty cool!

How to make a vector that follows a certain trend?

I have a set of data with over 4000 points. I want to exclude grooves from them, ideally from the point from which they start. The data look for example like this:
The problem with this is the noise I get at the top of the plateaus. I have an idea, in which I would take an average value of the most common within some boundaries (again, ideally sth like the red line here:
and then I would construct a temporary matrix, which would fill up one by one with Y if they are less than this average. If the Y(i) would rise above average, the matrix would find its minima and compare it with the global minima. If the temporary matrix's minima wouldn't be sth like 80% of the global minima, it would be discarded as noise.
I've tried using mean(Y), interpolating and fitting it in a polynomial (the green line) - none of those method would cut it to the point I would be satisfied.
I need this to be extremely robust and it doesn't need to be quick. The top and bottom values can vary a lot, as well as the shape of the plateaus. The groove width is more or less the same.
Do you have any ideas? Again, the point is to extract the values that would make the groove.
How about a median filter?
Let's define some noisy data similar to yours, and plot it in blue:
x = .2*sin((0:9999)/1000); %// signal
x(1000:1099) = x(1000:1099) + sin((0:99)/50*pi); %// noise: spike
x(5000:5199) = x(5000:5199) - sin((0:199)/100*pi); %// noise: wider spike
x = x + .05*sin((0:9999)/10); %// noise: high-freq ripple
plot(x)
Now apply the median filter (using medfilt2 from the Image Processing Toolbox) and plot in red. The parameter k controls the filter memory. It should chosen to be large compared to noise variations, and small compared to signal variations:
k = 500; %// filter memory. Choose as needed
y = medfilt2(x,[1 k]);
hold on
plot(y, 'r', 'linewidth', 2)
In case you don't have the image processing toolbox and can't use medfilt2 a method that's more manual. Skip the extreme values, and do a curve fit with sin1 as curve type. Note that this will only work if the signal is in fact a sine wave!
x = linspace(0,3*pi,1000);
y1 = sin(x) + rand()*sin(100*x).*(mod(round(10*x),5)<3);
y2 = 20*(mod(round(5*x),5) == 0).*sin(20*x);
y = y1 + y2; %// A messy sine-wave
yy = y; %// Store the messy sine-wave
[~, idx] = sort(y);
y(idx(1:round(0.15*end))) = y(idx(round(0.15*end))); %// Flatten out the smallest values
y(idx(round(0.85*end):end)) = y(idx(round(0.85*end)));%// Flatten out the largest values
[foo goodness output] = fit(x.',y.', 'sin1'); %// Do a curve fit
plot(foo,x,y) %// Plot it
hold on
plot(x,yy,'black')
Might not be perfect, but it's a step in the right direction.