I want to fit a polynomial to noisy data so that the approximated polynomial is always >= the original data. For example:
x = linspace (-2, 6);
y = (x-2).^2 + 1 + 2 * randn (size (x));
function ret = delta (P, x, y)
yP = polyval (P, x);
d = yP - y;
d (d < 0) *= 1000;
ret = sumsq (d);
endfunction
P0 = polyfit (x, y, 2);
f = #(P) delta (P, x, y);
[P, FVAL] = sqp (P0, f)
xi = linspace (min(x), max(x), 100);
yi = polyval (P, xi);
plot(x, y, xi, yi);
grid on
Is there a better way/method which also works with higher order polynomials?
The easies way would be to just use polyfit and then calculate max(y-yi) and add this as offset but this wouldn't be optimal...
EDIT: I want to use GNU OCtave but added "matlab" as tag because the language and functions are similiar.
EDIT: based on tvo's answer and real data:
x = [10 20 30 40 50 60 80 100];
y = [0.2372, 0.1312, 0.0936, 0.0805, 0.0614, 0.0512, 0.0554, 0.1407];
function ret = delta (P, x, y)
ret = sumsq (polyval (P, x) - y);
endfunction
f = #(P) delta (P, x, y);
h = #(P) polyval(P, x) - y;
P0 = polyfit (x, y, 3);
[P] = sqp (P0, f, [], h)
xi = linspace (min(x), max(x));
yi = polyval (P0, xi);
yio = polyval (P, xi);
plot(x, y, xi, yi, ";initial guess;", xi, yio, ";optimized;");
grid on
but as you can see, the optimized and evaluated poly has points < the orginal point which isn't allowed.
your method seems fine, and I see no reason it can't be used for higher-order polynomials actually. Please explain why if you think it can't be used.
You are using Octave's 'sqp' solver. Documentation is here: http://www.gnu.org/software/octave/doc/v4.0.1/Nonlinear-Programming.html
You may want to avoid multiplying the error by an arbitrary number (1000 in your example) when it is negative. This may fail for different data sets, especially if they are larger, i.e. more data points.
You could try to use the non-linear inequality constraint options that Octave's 'sqp' offers, i.e. the h(x)>=0 (see doc).
As objective function phi you could use a square norm error, as in your example, and add constraints of the form h(x)>=0 for every data point. Note that 'x' would be the polynomial coefficients you want to fit and h(x) is the polynomial evaluated at a specific data point.
For example:
phi = #(P) delta_mod (P, x, y); % mod: Don't increase the importance of negative residuals!!
h = #(P) polyval(P, x1) - y1;
Psol = sqp(P0, phi, [], h);
Notice that the constraint function 'h' ensures that the polynomial will lie above (x1,y1), and the objective function 'phi' will try to keep it as close as possible. You can extend 'h' to contain one constraint for every data point in your set (see doc).
Related
I have stumbled upon this matlab code that solves this ODE
y'''(t) + a y(t) = -b y''(t) + u(t)
but I am confused by the ode_system function definition, specifically by the y(2) y(3) part. I would greatly appreciate if someone can shed some light
y(2) y(3) part in the ode_system function confuses me and how it contributes to overaal solution
% Define the parameters a and b
a = 1;
b = 2;
% Define the time horizon [0,1]
time_horizon = [0, 1];
% Define the initial conditions for y, y', and y''
initials = [0; 0; 0];
% Define the function handle for the input function u(t)
%sin(t) is a common example of a time-varying function.
% You can change the definition of u to any other function of time,
% such as a constant, a step function, or a more complex function, depending on your needs
u = #(t) sin(t);
% Define the function handle for the system of ODEs
odefunction = #(t, y) ode_system(t, y, a, b, u);
% Solve the ODEs using ode45
[t, y] = ode45(odefunction, time_horizon, initials);
% Plot the solution
plot(t, y(:,1), '-', 'LineWidth', 2);
xlabel('t');
ylabel('y');
function dydt = ode_system(t, y, a, b, u)
%Define the system of ODEs
dydt = [y(2); y(3); -b*y(3) + u(t) - a*y(1)];
end
This is more of a maths question than a Matlab one.
We would like to rewrite our ODE equation so that there is a single time derivative on the left-hand side and no derivatives on the right.
Currently we have:
y'''(t)+ay(t)=-by''(t)+u(t)
By letting z = y' and x = z' (= y''), we can rewrite this as:
x'(t)+a y(t)=-b x(t)+u(t)
So now we have 3 equations in the form:
y' = z
z' = x
x' = -b * x + u - a *y
We can also think of this as a vector equation where v = (y, z, x).
The right-hand side would then be,
v(1)' = v(2)
v(2)' = v(3)
v(3)' = -b * v(3) + u - a * v(1)
which is what you have in the question.
I updated the question to clarify it more. Here is a graph:
For the curve in the attached photo, I hope to draw the curve. I have its equation and it is after simplification will be like this one
% Eq-2
(b*Y* cos(v) + c - k*X*sin(v))^2 + ...
sqrt(k*X*(cos(v) + 1.0) + b*Y*sin(v))^2) - d = 0.0
Where:
v = atan((2.0*Y)/X) + c
and b, c, d and k are constants.
from the attached graph,
The curve is identified in two points:
p1 # (x=0)
p2 # (y=0)
I a new on coding so accept my apologize if my question is not clear.
Thanks
So, after your edit, it is a bit more clear what you want.
I insist that your equation needs work -- the original equation (before your edit) simplified to what I have below. The curve for that looks like your plot, except the X and Y intercepts are at different locations, and funky stuff happens near X = 0 because you have numerical problems with the tangent (you might want to reformulate the problem).
But, after checking your equation, the following code should be helpful:
function solve_for_F()
% graininess of alpha
N = 100;
% Find solutions for all alphae
X = zeros(1,N);
options = optimset('Display', 'off');
alpha = linspace(0, pi/2, N);
x0 = linspace(6, 0, N);
for ii = 1:numel(alpha)
X(ii) = fzero(#(x)F(x, alpha(ii)), x0(ii), options);
end
% Convert and make an X-Y plot
Y = X .* tan(alpha);
plot(X, Y,...
'linewidth', 2,...
'color', [1 0.65 0]);
end
function fval = F(X, alpha)
Y = X*tan(alpha);
% Please, SIMPLIFY in the future
A = 1247745517111813/562949953421312;
B = 4243112111277797/4503599627370496;
V = atan2(2*Y,X) + A;
eq2 = sqrt( (5/33*( Y*sin(V) + X/2*(cos(V) + 1) ))^2 + ...
(5/33*( Y*cos(V) - X/2* sin(V) ))^2 ) - B;
fval = eq2;
end
Results:
So, I was having fun with this (thanks for that)!
Different question, different answer.
The solution below first searches for the constants causing the X and Y intercepts you were looking for (p1 and p2). For those constants that best fit the problem, it makes a plot, taking into account numerical issues.
In fact, you don't need eq. 1, because that's true always for any curve -- it's just there to confuse you, and problematic to use.
So, here it is:
function C = solve_for_F()
% Points of interest
px = 6;
py = 4.2;
% Wrapper function; search for those constants
% causing the correct X,Y intercepts (at px, py)
G = #(C) abs(F( 0, px, C)) + ... % X intercept at px
abs(F(py, 0, C)); % Y intercept at py
% Initial estimate, based on your original equation
C0 = [5/33
1247745517111813/562949953421312
4243112111277797/4503599627370496
5/66];
% Minimize the error in G by optimizing those constants
C = fminsearch(G, C0);
% Plot the solutions
plot_XY(px, py, C);
end
function plot_XY(xmax,ymax, C)
% graininess of X
N = 100;
% Find solutions for all alphae
Y = zeros(1,N);
X = linspace(0, xmax, N);
y0 = linspace(ymax, 0, N);
options = optimset('Display', 'off',...,...
'TolX' , 1e-10);
% Solve the nonlinear equation for each X
for ii = 1:numel(X)
% Wrapper function for fzero()
fcn1 = #(y)F(y, X(ii), C);
% fzero() is probably the fastest and most intuitive
% solver for this problem
[Y(ii),~,flag] = fzero(fcn1, y0(ii), options);
% However, it uses an algorithm that easily diverges
% when the function slope is large. For those cases,
% solve with fminsearch()
if flag ~= 1
% In this case, the minimum of the absolute value
% is searched for (which should be zero)
fcn2 = #(y) abs(fcn1(y));
Y(ii) = fminsearch(fcn2, y0(ii), options);
end
end
% Now plot the X,Y solutions
plot(X, Y,...
'linewidth', 2,...
'color', [1 0.65 0]);
xlabel('X'), ylabel('Y')
axis([0 xmax+.1 0 ymax+.1])
end
function fval = F(Y, X, C)
% Unpack constants
b = C(1); d = C(3);
c = C(2); k = C(4);
% pre-work
V = atan2(2*Y, X) + c;
% Eq. 2
fval = sqrt( (b*Y*sin(V) + k*X*(cos(V) + 1))^2 + ...
(b*Y*cos(V) - k*X* sin(V) )^2 ) - d;
end
I need to fit data e.g. x, y, CI (where CI is confidence index of y) in Matlab.
Now, I use this code:
pf = polyfit(x, y, 2);
x1 = min(x):.1:max(x);
y1 = polyval(pf, x1);
figure
hold on
errorbar(x, y, CI, 'ko');
plot(x1, y1, 'k');
hold off
Of course, the fit comes out of some errors bars, and it's correct.
I would like obtain a fit curve closer to the points with a low confidence index, and discard the points with a high confidence index.
Thank you and bye,
Giacomo
What you are looking for are Weighted Least Squares. You can compute them with the function lscov. There is a nice example in its help page, but I'll try to make it clearer.
Let us construct a simple parabola, with a corrupted point
x = (0:0.1:1)';
y = 0.5*x.^2;
y(5) = 3*y(5);
and give some weights
w = ones(size(y));
w(5) = 0.1;
Next build the Vandermonde matrix (see here for the code) and solve the system
%// V = [x.^2 x ones(size(x))];
V = bsxfun(#power, x, 2:-1:0);
coeff = lscov(V, y, w);
The estimated coefficients, with and without the weights, are
x^2 x 1
with weights [0.4797 0.0186 -0.0004]
no weights [0.3322 0.1533 -0.0034]
Note that in your case w will have to be inverted.
If you don't like to build the Vandermonde matrix, and you have a license for the Curve Fitting Toolbox, you can use the following code
ft = fittype('poly2');
opts = fitoptions('Method', 'LinearLeastSquares');
opts.Weights = w;
fitresult = fit(x, y, ft, opts);
and you'll obtain the same result.
I'm trying to get Matlab to take this as a function of x_1 through x_n and y_1 through y_n, where k_i and r_i are all constants.
So far my idea was to take n from the user and make two 1×n vectors called x and y, and for the x_i just pull out x(i). But I don't know how to make an arbitrary sum in MATLAB.
I also need to get the gradient of this function, which I don't know how to do either. I was thinking maybe I could make a loop and add that to the function each time, but MATLAB doesn't like that.
I don't believe a loop is necessary for this calculation. MATLAB excels at vectorized operations, so would something like this work for you?
l = 10; % how large these vectors are
k = rand(l,1); % random junk values to work with
r = rand(l,1);
x = rand(l,1);
y = rand(l,1);
vals = k(1:end-1) .* (sqrt(diff(x).^2 + diff(y).^2) - r(1:end-1)).^2;
sum(vals)
EDIT: Thanks to #Amro for correcting the formula and simplifying it with diff.
You can solve for the gradient symbolically with:
n = 10;
k = sym('k',[1 n]); % Create n variables k1, k2, ..., kn
x = sym('x',[1 n]); % Create n variables x1, x2, ..., xn
y = sym('y',[1 n]); % Create n variables y1, y2, ..., yn
r = sym('r',[1 n]); % Create n variables r1, r2, ..., rn
% Symbolically sum equation
s = sum((k(1:end-1).*sqrt((x(2:end)-x(1:end-1)).^2+(y(2:end)-y(1:end-1)).^2)-r(1:end-1)).^2)
grad_x = gradient(s,x) % Gradient with respect to x vector
grad_y = gradient(s,y) % Gradient with respect to y vector
The symbolic sum and gradients can be evaluated and converted to floating point with:
% n random data values for k, x, y, and r
K = rand(1,n);
X = rand(1,n);
Y = rand(1,n);
R = rand(1,n);
% Substitute in data for symbolic variables
S = double(subs(s,{[k,x,y,r]},{[K,X,Y,R]}))
GRAD_X = double(subs(grad_x,{[k,x,y,r]},{[K,X,Y,R]}))
GRAD_Y = double(subs(grad_y,{[k,x,y,r]},{[K,X,Y,R]}))
The gradient function is the one overloaded for symbolic variables (type help sym/gradient) or see the more detailed documentation online).
Yes, you could indeed do this with a loop, considering that x, y, k, and r are already defined.
n = length(x);
s = 0;
for j = 2 : n
s = s + k(j-1) * (sqrt((x(j) - x(j-1)).^2 + (y(j) - y(j-1)).^2) - r(j-1)).^2
end
You should derive the gradient analytically and then plug in numbers. It should not be too hard to expand these terms and then find derivatives of the resulting polynomial.
Vectorized solution is something like (I wonder why do you use sqrt().^2):
is = 2:n;
result = sum( k(is - 1) .* abs((x(is) - x(is-1)).^2 + (y(is) - y(is-1)).^2 - r(is-1)));
You can either compute gradient symbolically or rewrite this code as a function and make a standard +-eps calculation. If you need a gradient to run optimization (you code looks like a fitness function) you could use algorithms that calculate them themselves, for example, fminsearch can do this
I have a very simple problem.
I have
x=[ 10 25 50];
y=[ 1.2 3 7.5];
I know my curve fitting function
f(x)=(a*x+1)/(bx+c);
How can I get coefficient(a,b,c) solve in matlab and also plot this curve?
Rearrange y = f(x) to make a, b, and c the unknowns:
y = (ax + 1) / (bx + c)
y(bx + c) = ax + 1
ax - bxy - cy = -1;
This describes a system of simultaneous linear equations in a, b, and c when you substitute your three paired values of x and y.
x = [10, 20, 100];
y = [1.2, 0.7, 0.4];
coeffs = [x', (-x.*y)', -y'];
knowns = [-1, -1, -1]';
v = coeffs \ knowns; % v is [a; b; c]
Now you have the coefficients a, b, and c so you can plot the function.
Addendum: plotting
To plot a function, first choose the x-values of the data points
xt = 1:100;
Then calculate the y-values (assuming you've already got a, b, c)
yt = (a*x + 1) ./ (b*x + c)
Then just plot them!
plot(xt, yt);
Read the Matlab help on the plot function for customizing the style of the plot.