Piece-wise non-linear fitting with fminsearch - issue with quadratic function
2 views (last 30 days)
Show older comments
Hello everybody.
I have a series of experimental data (tabella_raw.txt, column #4) for which I want to find the best interpolation function. If you have a look at the experimental data named 'qse' you can see that the best option is a piece-wise interpolation: three functions, in particular, the first part quadratic (from 0 to 1 h), as well as the second (from 1 to 2), and the third is hyperbolic (from 2 to end). The function must be continuous. I tried to find at first some suitable expressions for the three parts and then, by adopting fminsearch that minimizes the error between the two curves, and I got some kind of interpolation curve (the red one). In blue you can see the experimental data.
However, there is something wrong or better to say, something I don't like. At 1 and 2 (yellow dots) I want to have the maximum and the minimum values of the function. Is there any possibile way to set up some paramters contraints to obtain something like the second picture?
I attach the matlab code, as well.
For any doubts, please write me.
Thanks in advance.
Maja
10 Comments
Mathieu NOE
on 15 Jan 2024
tada !
now the full monty with fminsearch at the rescue
see answer section
Accepted Answer
Mathieu NOE
on 15 Jan 2024
as announced above in the teaser , please find now the full code with fminsearch for the fine tuning
I tooked a while to make it work as I wanted but I am happy to have achieved this (in parallel to my official work today :))
clc
clear all
close all
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% INTERPOLAZIONE CURVE DINAMICO %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
data=readmatrix('tabella_raw.txt');
tsi=data(:,1); tse=data(:,2); qsi=data(:,3); qse=data(:,4);
ti=data(:,5); to=data(:,6);
%__________________________________________________________________________
% INTERPOLAZIONE CURVE
%__________________________________________________________________________
% usare la scala x ogni ora per fitting corretto
n = 500;
ind = (1:n);
x=(ind/60)';
dT = 10; % delta T impulso (10°)
qse = qse(ind);
%% flusso termico esterno qse
%% step1 : best expo fit of first segment to compute exactly t1 with best accuracy
[m1,i1] = max(qse); % initial guess (i1) where is t1 , but this is not yet accurate enough
[k, yInf, y0, ~] = fitExponential(x(1:i1), qse(1:i1));
yFit = yInf + (y0-yInf) * exp(-k*(x-x(1)));
% refined t1 / x1 value (important for the step2 good performance
ind = find(yFit>qse);
ind = ind(ind>i1);
ind = ind(1);
x1 = x(ind); % or t1 if you prefer
%% step2 : do the general equation fit
% sigmoid a parameter (fixed paramter)
a_sig = -1000; % a large negative value so the sigmoid is like a steped window (sharp transition); -1000 is fine
% compute some vlaues we need to supply as IC for fminsearch
[m1,i1] = max(qse);
s1 = mysigmoid(x,x1,a_sig);
[m2,i2] = min(qse);
x2 = x(i2); % or t2 if you prefer
s2 = mysigmoid(x,x2,a_sig);
% curve fit using fminsearch
smooth_factor = 0.1; % 1 is too large , 0.1 is good
b1 = -2; % exponant term
b2 = -2; % exponant term
b3 = -1; % exponant term
b4 = -1; % exponant term
f = @(a,b,c,d,e,f,g,h,l,m,x) a*(1-exp(b*x)).*s1 + (c - d*(1-exp(e*(abs(x-x1)).^m))).*(1-s1).*s2 + (f*exp(g*(x-x2) + b4*(abs(x-x2)).^l)).*(1-s2);
obj_fun = @(p) norm(f(p(1),p(2),p(3),p(4),p(5),p(6),p(7),p(8),p(9),p(10),x)-qse) + smooth_factor*sum(abs(gradient(f(p(1),p(2),p(3),p(4),p(5),p(6),p(7),p(8),p(9),p(10),x))));
IC = [m1 b1 m1 (m1-m2) b2 m2 b3 b4 0.25 0.75];
sol = fminsearch(obj_fun, IC);
a_sol = sol(1);
b_sol = sol(2);
c_sol = sol(3);
d_sol = sol(4);
e_sol = sol(5);
f_sol = sol(6);
g_sol = sol(7);
h_sol = sol(8);
l_sol = sol(9);
m_sol = sol(10);
yfit = f(a_sol,b_sol,c_sol,d_sol,e_sol,f_sol,g_sol,h_sol,l_sol,m_sol, x);
plot(x,qse)
hold on
plot(x,yfit)
hold off
legend('data','fminsearch fit');
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [k, yInf, y0, yFit] = fitExponential(x, y)
% FITEXPONENTIAL fits a time series to a single exponential curve.
% [k, yInf, y0] = fitExponential(x, y)
%
% The fitted curve reads: yFit = yInf + (y0-yInf) * exp(-k*(x-x0)).
% Here yInf is the fitted steady state value, y0 is the fitted initial
% value, and k is the fitted rate constant for the decay. Least mean square
% fit is used in the estimation of the parameters.
%
% Outputs:
% * k: Relaxation rate
% * yInf: Final steady state
% * y0: Initial state
% * yFit: Fitted time series
%
% Last modified on 06/26/2012
% by Jing Chen
% improve accuracy by subtracting large baseline
yBase = y(1);
y = y - y(1);
fh_objective = @(param) norm(param(2)+(param(3)-param(2))*exp(-param(1)*(x-x(1))) - y, 2);
initGuess(1) = -(y(2)-y(1))/(x(2)-x(1))/(y(1)-y(end));
initGuess(2) = y(end);
initGuess(3) = y(1);
param = fminsearch(fh_objective,initGuess);
k = param(1);
yInf = param(2) + yBase;
y0 = param(3) + yBase;
yFit = yInf + (y0-yInf) * exp(-k*(x-x(1)));
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function y = mysigmoid(x,c,a)
% sigmoid evaluates a simple sigmoid function along x:
%
% ______1________
% y = -a(x-c)
% 1 + e^
%
%% Syntax
%
% y = sigmoid(x)
% y = sigmoid(x,c)
% y = sigmoid(x,c,a)
%
y = 1./(1 + exp(-a.*(x-c)));
end
0 Comments
More Answers (0)
See Also
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!