Clear Filters
Clear Filters

Fit different (but connected) equations to connected intervals, with ~arbitrary function on each interval

3 views (last 30 days)
I have some data, with several different intervals (3 or 4 divisions), where I would like to fit a (connected) curve that follows the appropriate model in each interval. The outer models are connecting expontential decay functions, with decay lengths dependent on x, and the inner model is a simple line, with a tanh function connecting the line with the exponential decay functions. To avoid too much complication, I will reduce my question to its essence.
In a simple example, imagine I have data points that are two straight lines, with a knot in the middle. Taking the matlab example and modifying it slightly, lets say this is my function to use in the fit:
function y = profile_fit(x, slope1, slope2, amplitude, knot_location)
y = zeros(size(x));
for i = 1:length(x)
if x(i) < knot_location
y(i) = amplitude + slope1.* x(i);
else
y(i) = (amplitude+ slope1.*knot_location) + slope_2.* (x(i)-knot_location);
end
end
end
The knot location and amplitude is unknown, and the function follows slope1 with amplitude before the knot, and then slope2 after the knot, with the corresponding amplitude and position modified to connect the line appropriately with the first line.
In this case, I don't know the knot location or amplitude, but can (probably) provide slope1 and slope2. To do the fit, I'd like to be able to run it such that:
% Define (arbitrary):
slope1 = 5;
slope2 = -2;
% Define fit based on function:
profile_ft = fittype('profile_fit2(x, slope1, slope2, amplitude, knot_location)','problem',{'slope1','slope2'})
% Create some data with noise
noise = randn(100,1);
x_data = linspace(0,100,100);
x_data = x_data + noise;
knot = 52;
ampl = 11;
y_data = profile_fit(x_data, slope1, slope2, ampl, knot)
% This is the fit that I would like:
the_fit = fit(x_data, y_data, profile_ft, 'problem',{'slope1','slope2'},'StartPoint',{10, 50});
% parameters to return: amplitude (should be 11), knot_location (should be 52)
works as intended.
However, instead I get a complicated error, likely because the knot_location is not nicely defined this way.
Error using fittype/testCustomModelEvaluation (line 12)
Expression profile_fit(x, slope1, slope2, amplitude, knot_location) is not a valid MATLAB expression, has non-scalar
coefficients, or cannot be evaluated:
Error in fittype expression ==> profile_fit(x, slope1, slope2, amplitude, knot_location)
??? Undefined function 'profile_fit' for input arguments of type 'double'.
Error in fittype>iCreateFittype (line 373)
testCustomModelEvaluation( obj );
Error in fittype (line 330)
obj = iCreateFittype( obj, varargin{:} );
Error in profile_fit (line 6)
profile_ft = fittype('profile_fit(x, slope1, slope2, amplitude, knot_location)','problem',{'slope1','slope2'})
Caused by:
Error using fittype/evaluate (line 106)
Error in fittype expression ==> profile_fit(x, slope1, slope2, amplitude, knot_location)
??? Undefined function 'profile_fit2' for input arguments of type 'double'.
Aside from trying to use 'fit', I've come across the possibilty of using 'fminsearch' for simultaneous fitting of two functions with a knot in the middle, but this example knew where the knot is... Whereas for me, I'd like to determine that from the data.
Can anyone please help? Thank you.

Accepted Answer

Varun
Varun on 23 Aug 2023
Hi mack,
I understand that you have several different intervals and you would like to fit a (connected) curve that follows the appropriate model in each interval. And you tried experimenting matlab example with some modifications but you are getting some errors.
I have debugged the code you provided and fixed the errors. Some majors errors were:
  1. In profile_fit function you are taking input as slope2 but using it as slope_2.
  2. In the following section, noise is 100*1 column vector where as linspace(0,100,100) is 1*100 row vector and adding them gives you 100*100 matrix which lead to unusual errors further.
noise = randn(100,1);
x_data = linspace(0,100,100);
x_data = x_data + noise;
3.You should use 'problem',{slope1,slope2} instead of 'problem',{'slope1','slope2'} and 'StartPoint',[10 50] instead of 'StartPoint',{10, 50} in the fit function.
Here is the updated code:
function y = profile_fit(x, slope1, slope2, amplitude, knot_location)
y = zeros(size(x));
for i = 1:length(x)
if x(i) < knot_location
y(i) = amplitude + slope1.* x(i);
else
y(i) = (amplitude+ slope1.*knot_location) + slope2.* (x(i)-knot_location);
end
end
end
% Define (arbitrary):
slope1 = 5;
slope2 = -2;
% Define fit based on function:
profile_ft = fittype('profile_fit(x, slope1, slope2, amplitude, knot_location)','problem',{'slope1','slope2'})
% Create some data with noise
noise = randn(100,1);
x_data = linspace(0,100,100)'; % transpose to make it 100*1 column vector.
x_data = x_data + noise;
knot = 52;
ampl = 11;
y_data = profile_fit(x_data, slope1, slope2, ampl, knot)
% This is the fit that I would like:
the_fit = fit(x_data, y_data, profile_ft, 'problem',{slope1 slope2},'StartPoint',[10 50]);
% parameters to return: amplitude (should be 11), knot_location (should be 52)
disp(the_fit)
Here is the output of amplitude and knot_location with the expected value to be 11 and 52 respectively.
General model:
the_fit(x) = profile_fit(x, slope1, slope2, amplitude, knot_location)
Coefficients (with 95% confidence bounds):
amplitude = 11 (11, 11)
knot_location = 52 (52, 52)
Problem parameters:
slope1 = 5
slope2 = -2

More Answers (0)

Products


Release

R2020a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!