在Matlab中同时拟合数据以估计NaN数据的公共参数

时间:2019-04-26 04:59:39

标签: matlab optimization missing-data

我有在四个时间点(t)内测量的三个浓度(c)的数据(y)。
因此数据将像:

  

在c1下:y11,y12,y13,y14(在4个时间点测量)

     

在c2:y21,y12,y23,y24下

     

在c3下:y31,y32,y33,y34

我想做的是通过同时拟合所有浓度不同的所有这些数据度量来估计两个参数cd
但是,其中一些值为NaN。例如,

  

在c2:y21,y12,NaN,NaN下

     

在c3下:y31,y32,y33,NaN

这是我编写的Matlab代码。

%C contains the different concentration values (c1,c2,c3)
[fittedVals,errorVals]=lsqcurvefit(@(xEstimate,thours)model(xEstimate,t,C),initial,t,y,lb,ub);

function output= model(xEstimate,t,C)

intVals=repmat(10^5,3,1);%initial value of the ODE system

[~,values] = ode45(@(t,y)Equations(t,y,C),t,intVals);

function s=Equations(~,y,para)

    a=0.25;
    b=-0.1;
    k=xEstimate(1);
    d=xEstimate(2);
    concentration=para;%different concentrations

    s=zeros(3,1);
    s(1)=a*y(1)-y(1)*(((a-b)*(concentration(1)/k).^d)/((concentration(1)/k).^d-(b/(a)))); 
    s(2)=a*y(2)-y(2)*(((a-b)*(concentration(2)/k).^d)/((concentration(2)/k).^d-(b/(a)))); 
    s(3)=a*y(3)-y(3)*(((a-b)*(concentration(3)/k).^d)/((concentration(3)/k).^d-(b/(a)))); 



   end

output=values;
end

当数据不是NaN但缺少数据时,此代码将起作用,它会给出如下错误:

  

目标函数在初始点返回未定义的值。   lsqcurvefit无法继续。

在这里我该怎么解决?我应该将时间和y数据作为单元格数组输入吗?
如果是这样,我不太了解如何更改代码以使用单元格数组。
任何帮助表示赞赏。

1 个答案:

答案 0 :(得分:1)

您可以使用lsqnonlin代替lsqcurvefit。这使您在想要最小化的误差向量上具有更大的灵活性。

您可以确定每种浓度的每次测量的误差,然后将它们组合到要最小化的较大误差向量上。一个简单的例子是具有变化的幅度,频率和相位的正弦模型。假设我们知道相位,并想要找到振幅和频率。

模型是:

function y = model(t, A, w, phi)
    y = A.*sin(w.*t+phi); 
end

拟合过程的误差函数将获取测量数据和已知参数。确定某个参数集(由y_estimated给定)的lsqnonlin,并用测量值y_meas确定误差。对所有不同的浓度执行此操作,然后合并到一个误差向量中。由于y_meas中的某些值为NaN,而您想忽略它们,因此请从误差向量中将其删除。

function err = errorFun(params, y_meas, t, phi)

    % get w and phi from params
    A = params(1:3);
    w = params(4:6);

    % simulate model with params to estimate
    yest = model(t, A, w, phi);

    % determine error vector
    err = y_meas-yest; 
    err = err(:);           % make one vector
    err(isnan(err)) = [];   % remove NaNs
end

示例:

t = 0:0.5:4*pi;
A = rand(3,1);
w =  rand(3,1);
phi = rand(3,1);

y_true = A.*sin(w.*t+phi); % three sinusoids

% measured data
y_meas = y_true;
y_meas(randi([1 numel(y_meas)], 10,1)) = NaN; % set some values to NaN

% optimize
% p = [A;w];
p0 = [A;w;]+0.1; % small deviation from initial parameters, for the example
[p_estimated,a] = lsqnonlin(@(p) errorFun(p,y_meas,t,phi), p0); 

A_est = p_estimated(1:3);
w_est = p_estimated(4:6);

disp([A A_est w w_est])