Related
I am starting to learn numerical analysis using MATLAB in my course. so far we have covered polynomial interpolation (spline, polyfit, constraint spline, etc.) I was doing this practice question and I can not get the correct answer. I have uploaded the code I used and the question, where did I do wrong? thanks in advance!
close all; clear all; clc;
format long e
x = linspace(0,1,8);
xplot = linspace(0,1);
f = #(x) atan(x.*(x+1));
y_val = f(xplot);
c = polyfit(x,f(x),7);
p = polyval(c,0.7);
err = abs(f(0.7)-p)/(f(0.7))
The question I encountered is seen in the picture
After some playing around, it seems to be a matter of computing the absolute error instead of the relative absolute error.
The code below yields the desired answer. And yes, it is pretty unclear from the question which error is intended.
% Definitions
format long e
x = linspace(0,1,8)';
xplot= linspace(0,1);
f = #(x) atan(x.*(x+1));
y_val = f(xplot);
% Degree of polynomial
n = 7;
% Points to evaluate function
point1 = 0.5;
point2 = 0.7;
% Fit
c= polyfit(x,f(x),n);
% Evaluate
approxPoint1 = polyval(c, point1);
approxPoint2 = polyval(c, point2);
% Absolute errors
errPoint1 = abs( f(point1) - approxPoint1)
errPoint2 = abs( f(point2) - approxPoint2)
What you did wrong was :
mixing absolute and relative values when calculating errors to feed resulting variable err.
incorrectly placing abs() parentheses when calculating err: Your abs() only fixes the numerator, but then the denominator. To obtain |f(0.7)| you also need another abs(f(0.7))
for point x=0.7 instead of
err = abs(f(0.7)-p)/(f(0.7))
could well simply be
err = abs(f(.7)-p));
you only calculate err for assessment point 0.5 . In order to choose among the possible candidates of what seems to be a MATLAB Associate test multichoice answer, one needs err on 0.5 and err on 0.7 and then match the pair in the correct order, among all offered possible answers.
Although it's common practice to approach polynomial approximation of N points with an N-1 degree polynomial, it's often possible to approximate below satisfactory error with lower degree polynomials than N-1.
Lower degree polynomials means less calculations, less time spent approximating points. If one obtains a fair enough approximation with for instance a degree 4 polynomial, why waste time calculating an approximation with a higher degree all the way up to N-1?
Since the question does not tell what degree should have the approximating polynomial, you have to find it sweeping all polynomial degrees from 1 to up to a reasonable order.
The last error I found is that you have used linspace without specifying amount of points, MATLAB then takes by default 100 points hoping it's going to be ok.
Well, in your question 100 points is way too low an amount of points as I am going to show after supplying the following lines, as mentioned in previous point, sweeping all possible approximating polynomials, NOT on default 100 points you tacitly chose.
np=8; % numel(x) amount supplied points
N=1e3; % amount points x grid we build to measure f(x)
x= linspace(0,1,np);
xplot = linspace(0,1,N);
f = #(x) atan(x.*(x+1)); % function to approximate
y_val = f(xplot); % f(x)
xm=[.5 .7]; % points where to asses error
% finding poly coeffs
err1=zeros(2,np+2); % 1st row are errors on x=.5, 2nd row errors on x=.7
figure(1);
ax1=gca
hp1=plot(xplot,y_val)
grid on;
hp1.LineWidth=3;
hp1.Color='r';
hold on
for k=1:1:np+2
c = polyfit(x,f(x),k);
p_01 = polyval(c,xm(1));
err1(1,k) = abs(f(xm(1))-p_01);
% err(1,k) = abs((f(0.5)-p_05)/(f(0.5)))
p_02 = polyval(c,xm(2));
err1(2,k) = abs(f(xm(2))-p_02);
% err(2,k) = abs((f(0.7)-p_07)/(f(0.7)))
plot(x,polyval(c,x),'LineWidth',1.5); %'Color','b');
end
err1
.
.
The only pair of errors matching in the correct order are indeed those of polynomial order 7, but the total smallest error corresponds to the approximating polynomial of order 6.
What happens when taking linspace without defining a large enough amount of points? let's have a look:
np=8; % numel(x) amount supplied points
% N=1e3; % amount points x grid we build to measure f(x)
x= linspace(0,1,np);
xplot = linspace(0,1); %,N);
f = #(x) atan(x.*(x+1)); % function to approximate
y_val = f(xplot); % f(x)
xm=[.5 .7]; % points where to asses error
% finding poly coeffs
err1=zeros(2,np+2); % 1st row are errors on x=.5, 2nd row errors on x=.7
figure(1);
ax1=gca
hp1=plot(xplot,y_val)
grid on;
hp1.LineWidth=3;
hp1.Color='r';
hold on
for k=1:1:np+2
c = polyfit(x,f(x),k);
p_01 = polyval(c,xm(1));
err1(1,k) = abs(f(xm(1))-p_01);
% err(1,k) = abs(f(0.5)-p_05)/abs(f(0.5))
p_02 = polyval(c,xm(2));
err1(2,k) = abs(f(xm(2))-p_02);
% err(2,k) = abs(f(0.7)-p_07)/abs(f(0.7))
plot(x,polyval(c,x),'LineWidth',1.5); %'Color','b');
end
err1
With only 100 points all errors come up way too large, not a single error anywhere near 1e-5 or 1e-6.
This is why one couldn't tell which pair to go for, because all obtained values where at least 5 orders of magnitude away from landing zone.
I was about to include a plot with legend, but the visualization of this particular approach is in this case and in my opinion at best misleading, as in both plots for 100 and 1000 points, at 1st glance, both look as if the errors should be similar regardless of the amount of grid points used.
But as shown above 1e2 points cannot approximate the function,
it's like pitch dark, looking for something and pointing torch 180 from where we should be aiming at, not a chance to spot it.
Yet 1e3 grid points produce a pair of errors matching one of the possible answers, this is option D.
I hope it helps, thanks for reading my answer.
So I need to build a matrix of x and y coordinates. I have the x stored in one matrix called vx=0:6000; and y stored in Vy=repmat(300,1,6000);.
Values in x are 0,1,2,...,5999,6000.
Values in y are 300,300,...,300,300.
How do I build a "vector" with the x,y coordinates above?
It would look like this [(0,300);(1,300);...;(5999,300);(6000,300)].
After I finish doing this, I am going to want to find the distance between another fixed point x,y (that I will replicate 6000 times) and the vector above, in order to make a distance graph over time.
Thank you so much!
You can just use horizontal concatenation with []
X = [Vx(:), Vy(:)];
If you want to compute the distance between another point and every point in this 2D array, you could do the following:
point = [10, 100];
distances = sqrt(sum(bsxfun(#minus, X, point).^2, 2));
If you have R2016b or newer you can simply do
distances = sqrt(sum((X - point).^2, 2));
A slightly more elegant alternative (in my opinion) is the following:
Vx = (0:1:6000).';
C = [Vx 0*Vx+300]; % Just a trick to avoid the overly verbose `repmat`.
p = [10,100]; % Define some point of reference.
d = pdist2(C,p); % The default "distance type" is 'euclidian' - which is what you need.
This uses the pdist2 function, introduced in MATLAB 2010a, and requires the Statistics and Machine Learning Toolbox.
From an lsqcurve fit function I am trying to calculate the horizontal asymptote of a sigmoidal curve over the data range. The code I am using to do this and convert the output to double precision works and is as follows:
time = [0,0.166666666666667,0.333333333333333,0.500000000000000,0.666666666666667,0.833333333333333,1,1.16666666666667,1.33333333333333,1.50000000000000,1.66666666666667,1.83333333333333,2,2.16663888888889,2.33330555555556,2.49997222222222,2.66663888888889,2.83330555555556,2.99997222222222,3.16663888888889,3.33330555555556,3.49997222222222,3.66663888888889,3.83330555555556,3.99997222222222,4.16663888888889,4.33330555555556,4.49997222222222,4.66663888888889,4.83330555555556,4.99997222222222,5.16663888888889,5.33327777777778,5.49997222222222,5.66661111111111,5.83327777777778,5.99994444444444,6.16661111111111,6.33327777777778,6.49994444444444,6.66661111111111,6.83327777777778,6.99994444444444,7.16661111111111,7.33327777777778,7.49994444444444,7.66661111111111,7.83327777777778,7.99994444444444,8.16661111111111,8.33327777777778,8.49994444444445,8.66661111111111,8.83325000000000,8.99991666666667,9.16658333333333,9.33325000000000,9.49991666666667,9.66658333333333,9.83325000000000,9.99991666666667,10.1665833333333,10.3332500000000,10.4999166666667,10.6665833333333,10.8332500000000,10.9999166666667,11.1665833333333,11.3332500000000,11.4999166666667,11.6665833333333,11.8332222222222,11.9998888888889,12.1665555555556,12.3332222222222,12.4998888888889,12.6665555555556,12.8332222222222,12.9998888888889,13.1665555555556,13.3332222222222,13.4998888888889,13.6665555555556,13.8332222222222,13.9998888888889,14.1665555555556,14.3332222222222,14.4998888888889,14.6665555555556,14.8332222222222,14.9998611111111,15.1665277777778,15.3331944444444,15.4998611111111];
Abs=[0.0756333346168200,,0.0822666635115941,0.101066668828328,0.125066662828128,0.147433335582415,0.169966672857602,0.192199997603893,0.211166655023893,0.231433339416981,0.256133330365022,0.279866663118203,0.295566670596600,0.309466672440370,0.323400000731150,0.335233343144258,0.348299992581209,0.359700001776218,0.371433332562447,0.383333327869574,0.394433322052161,0.404166663686434,0.413666687905788,0.423499998946985,0.432733344535033,0.443266667425633,0.453899989525477,0.463699989020824,0.475066669285297,0.485300009449323,0.495999989410241,0.504966656366984,0.514033327500025,0.523766654233138,0.532566664119562,0.541033337513606,0.550333333512147,0.559733323752880,0.567733330031236,0.575466672579447,0.583399988710880,0.591433353722096,0.597833327949047,0.604699979225794,0.610233349104722,0.616733354826768,0.622800009946028,0.628666676580906,0.634199996789297,0.638966679573059,0.644799992442131,0.650733329355717,0.654566655556361,0.661333315074444,0.666366661588351,0.671233326196671,0.675699991484483,0.679433315992355,0.682733337084452,0.686333330969016,0.689166650176048,0.693133344252904,0.697366672257582,0.699333322544893,0.702233329415321,0.705400002499422,0.707699996729692,0.709999985992909,0.711533337831497,0.712800003588200,0.713633326192697,0.714666672050953,0.714533333977064,0.713999989132086,0.714266665279865,0.714066684246063,0.714266670246919,0.713733327885469,0.713066659867764,0.712200003365676,0.711600005626679,0.710400002698104,0.710399977862835,0.709799999992053,0.708366667230924,0.706933336953322,0.706766667465369,0.706199998656909,0.704266687234243,0.703333348035812,0.702399998903275,0.701133318245411,0.700166669984659,0.698799995084604,0.697700018684069];
u=linspace(0,15.49986111,500);
v=pchip(time,Abs,u);
p0 = [0.075633335,0.05,0.01]; %First value = Abs value at t=0
options=optimset('MaxFunEvals',10000,'MaxIter',5000,'Display','iter');
[p,error,residual]=lsqcurvefit(#gompertz,p0,time,Abs,[],[],options)
syms x t
f=p(1)*exp(-exp(((p(2)*exp(1))/p(1))*(p(3)-t)+1));
f1=diff(f,t); %Find first derivative of equation W wrt t
f2=diff(f,t,2); %Find second derivative wrt t and to allow inflection point to be defined
crit_pts = solve(f1); %Find local maxima and minima
crit_pts=double(crit_pts);
flimit = limit(f,inf); %Find find horizontal asymptote of function f by taking limit of f as x approaches infinity
function_horizontal_asymptote=double(limit);
The 'gompertz' function called is coded as follows:
function y = gompertz(p,t)
y=p(1)*exp(-exp(((p(2)*exp(1))/p(1))*(p(3)-t)+1));
According to the Matlab documentation I have read
flimit = limit(f,inf);
should yield the correct answer but and appear towards the end of the time course, however the result I get is (shown by the yellow marker):
Am I calculating this incorrectly?
Thank you for your time,
Laura
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.
I need to compute a moving average over a data series, within a for loop. I have to get the moving average over N=9 days. The array I'm computing in is 4 series of 365 values (M), which itself are mean values of another set of data. I want to plot the mean values of my data with the moving average in one plot.
I googled a bit about moving averages and the "conv" command and found something which i tried implementing in my code.:
hold on
for ii=1:4;
M=mean(C{ii},2)
wts = [1/24;repmat(1/12,11,1);1/24];
Ms=conv(M,wts,'valid')
plot(M)
plot(Ms,'r')
end
hold off
So basically, I compute my mean and plot it with a (wrong) moving average. I picked the "wts" value right off the mathworks site, so that is incorrect. (source: http://www.mathworks.nl/help/econ/moving-average-trend-estimation.html) My problem though, is that I do not understand what this "wts" is. Could anyone explain? If it has something to do with the weights of the values: that is invalid in this case. All values are weighted the same.
And if I am doing this entirely wrong, could I get some help with it?
My sincerest thanks.
There are two more alternatives:
1) filter
From the doc:
You can use filter to find a running average without using a for loop.
This example finds the running average of a 16-element vector, using a
window size of 5.
data = [1:0.2:4]'; %'
windowSize = 5;
filter(ones(1,windowSize)/windowSize,1,data)
2) smooth as part of the Curve Fitting Toolbox (which is available in most cases)
From the doc:
yy = smooth(y) smooths the data in the column vector y using a moving
average filter. Results are returned in the column vector yy. The
default span for the moving average is 5.
%// Create noisy data with outliers:
x = 15*rand(150,1);
y = sin(x) + 0.5*(rand(size(x))-0.5);
y(ceil(length(x)*rand(2,1))) = 3;
%// Smooth the data using the loess and rloess methods with a span of 10%:
yy1 = smooth(x,y,0.1,'loess');
yy2 = smooth(x,y,0.1,'rloess');
In 2016 MATLAB added the movmean function that calculates a moving average:
N = 9;
M_moving_average = movmean(M,N)
Using conv is an excellent way to implement a moving average. In the code you are using, wts is how much you are weighing each value (as you guessed). the sum of that vector should always be equal to one. If you wish to weight each value evenly and do a size N moving filter then you would want to do
N = 7;
wts = ones(N,1)/N;
sum(wts) % result = 1
Using the 'valid' argument in conv will result in having fewer values in Ms than you have in M. Use 'same' if you don't mind the effects of zero padding. If you have the signal processing toolbox you can use cconv if you want to try a circular moving average. Something like
N = 7;
wts = ones(N,1)/N;
cconv(x,wts,N);
should work.
You should read the conv and cconv documentation for more information if you haven't already.
I would use this:
% does moving average on signal x, window size is w
function y = movingAverage(x, w)
k = ones(1, w) / w
y = conv(x, k, 'same');
end
ripped straight from here.
To comment on your current implementation. wts is the weighting vector, which from the Mathworks, is a 13 point average, with special attention on the first and last point of weightings half of the rest.