2D data fit lsqcurvefit with NaN in grid

26 views (last 30 days)
Using Star Strider's solution to fit 2D data to a custom function: https://de.mathworks.com/matlabcentral/answers/119001-2d-data-fitting-surface I ran into the problem that it does not work if the z values contain NaNs.
take the exact same problem but exchange z with:
x = [1 2 4 6 8 10 13 17 21 25];
y = [0.2 0.5 1 2 4 7 10 14 18 22];
z =[1,0.6844,0.3048,NaN,0.1689,0.1432,0.1192,0.1015,0.09080,0.08410;1,0.7096,0.3595,0.2731,0.2322,0.2081,0.1857,0.1690,0.1590,0.1529;1,0.7451,0.4362,0.3585,0.3217,0.2999,0.2797,0.2648,NaN,0.2504;1,0.7979,0.5519,0.4877,0.4574,0.4394,NaN,0.4107,NaN,0.3994;1,NaN,0.6945,0.6490,NaN,0.6145,0.6027,0.5945,0.5896,0.5870;1,NaN,NaN,0.7758,NaN,0.7531,NaN,0.7410,0.7383,0.7368;1,0.9397,0.8647,0.8436,NaN,0.8278,0.8228,0.8195,0.8181,NaN;1,0.9594,0.9087,NaN,NaN,0.8839,0.8808,0.8791,NaN,NaN;1,0.9705,0.9342,0.9238,NaN,0.9165,0.9145,0.9133,NaN,NaN;NaN,0.9776,0.9502,0.9425,0.9390,0.9372,0.9358,NaN,NaN,NaN]
% to fit, Star Strider suggests:
[X Y] = meshgrid(x,y);
% Create input independent variable (10 x 10 x 2):
XY(:,:,1) = X;
XY(:,:,2) = Y;
% Create Objective Function:
surfit = @(B,XY) B(1)*exp(B(2).*XY(:,:,1)) + (1 - exp(B(3).*XY(:,:,2)));
% surfit = @(B,XY) exp(B(1).*XY(:,:,1)) + (1 - exp(B(2).*XY(:,:,2)));
% Do Regression
B = lsqcurvefit(surfit, [0.5 -0.5 -0.5], XY, z, [0 -10 -10], [1 10 10])
it all works nicely without NaNs in z.
with NaNs, the fit gives an error. Is there any way to only give the valid value tuples to lsqcurvefit?
I tried indexing the NaNs but XY(nonNaNs,:) is not allowed by matlab...
Interpolating the missing data is not an option
Thank you for the assistance.

Accepted Answer

Bjorn Gustavsson
Bjorn Gustavsson on 3 Jun 2022
Edited: Bjorn Gustavsson on 3 Jun 2022
You could try doing this with lsqnonlin:
x = [1 2 4 6 8 10 13 17 21 25];
y = [0.2 0.5 1 2 4 7 10 14 18 22];
z =[1,0.6844,0.3048,NaN,0.1689,0.1432,0.1192,0.1015,0.09080,0.08410;1,0.7096,0.3595,0.2731,0.2322,0.2081,0.1857,0.1690,0.1590,0.1529;1,0.7451,0.4362,0.3585,0.3217,0.2999,0.2797,0.2648,NaN,0.2504;1,0.7979,0.5519,0.4877,0.4574,0.4394,NaN,0.4107,NaN,0.3994;1,NaN,0.6945,0.6490,NaN,0.6145,0.6027,0.5945,0.5896,0.5870;1,NaN,NaN,0.7758,NaN,0.7531,NaN,0.7410,0.7383,0.7368;1,0.9397,0.8647,0.8436,NaN,0.8278,0.8228,0.8195,0.8181,NaN;1,0.9594,0.9087,NaN,NaN,0.8839,0.8808,0.8791,NaN,NaN;1,0.9705,0.9342,0.9238,NaN,0.9165,0.9145,0.9133,NaN,NaN;NaN,0.9776,0.9502,0.9425,0.9390,0.9372,0.9358,NaN,NaN,NaN];
[X Y] = meshgrid(x,y);
idxOK = isfinite(z(:)); % find the points with finite z
% Define a residual-function
surf_res = @(B,X,Y,Z) Z - (B(1)*exp(B(2).*X) + (1 - exp(B(3).*Y)));
% Fit only using the finite-valued points
B = lsqnonlin(@(B) surf_res(B,X(idxOK),Y(idxOK),z(idxOK)), [1,1,1]);
% Check the results
plot3(X,Y,z,'r*')
hold on
surf(X,Y,-surf_res(B,X,Y,zeros(size(X))))
But it might be neater with a separation of the surface-function and the residual-function:
surf_fcn = @(B,X,Y) (B(1)*exp(B(2).*X) + (1 - exp(B(3).*Y)));
res_fcn = @(B,X,Y,Z,fcn) Z - fcn(B,X,Y);
B2 = lsqnonlin(@(B) res_fcn(B,X(idxOK),Y(idxOK),z(idxOK),surf_fcn), [1,1,1]);
plot3(X,Y,z,'r*')
hold on
surf(X,Y,surf_fcn(B2,X,Y))
You can constrain the search-space similarly with lsqnonlin as with lsqcurvefit. In this case the surface-function doesn't seem ideal, but I guess it is a test-example.
HTH

More Answers (0)

Products


Release

R2019b

Community Treasure Hunt

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

Start Hunting!