Wolfram Research

Function Repository Resource:

MultiNonlinearModelFit

Source Notebook

Fit multiple datasets with multiple expressions that share parameters

Contributed by: Sjoerd Smit

ResourceFunction["MultiNonlinearModelFit"][{dat1,dat2,},{form1,form2,},{β1,},{x1,}]

is a generalized form of NonlinearModelFit where each dataset dati is fitted with expression formi. The expressions are allowed to share parameter values βi with each other.

ResourceFunction["MultiNonlinearModelFit"][{dat1,dat2, }, <|"Expressions"{form1,form2,}, "Constraints"cons |>, {β1,},{x1,}]

fits the model subject to parameter constraints cons.

Details and Options

ResourceFunction["MultiNonlinearModelFit"] works by recasting the data and fitting forms into a form suitable for NonlinearModelFit.
The datasets dati must all be numeric matrices.
The number of datasets dati must match the number of expressions formi.
The parameters βi can be specified in the same way as in NonlinearModelFit. ResourceFunction["MultiNonlinearModelFit"] also inherits all options from NonlinearModelFit.
Internally, the datasets dati are joined together into a single matrix, so if a Weights vector is specified as an option, it should have the same length as Join[dat1,dat2,]. It is also possible to specify a Weights vector that has length matching the number of datasets. There is a special option Weights "InverseLengthWeights" to specify that each dataset should weigh equally in the fit, regardless of the number of points in each. Finally, it is also possible to specify the Weights as a list of vectors matching the input data.
The returned model uses the symbol n (\[FormalN]) to switch between the different expressions formi. This symbol should therefore be avoided by the user when using ResourceFunction["MultiNonlinearModelFit"]. This symbol can be changed with the "DatasetIndexSymbol" option

Examples

Basic Examples

Fit some random data to simple linear models with shared slope parameter:

In[1]:=
data = RandomVariate[BinormalDistribution[0.7], {2, 100}];
data[[2, All, 2]] += 2.;

ResourceFunction["MultiNonlinearModelFit"][
 data,
  {a x + b1, a x + b2}, {a, b1, b2}, {x}]
Out[3]=
In[4]:=
Show[
 ListPlot[data],
 Plot[Evaluate[Table[Normal[%], {\[FormalN], Length[data]}]], {x, -5, 5}]
 ]
Out[4]=

Fit two Gaussian peaks with a shared location parameter:

In[5]:=
xvals = Range[-5, 5, 0.1];
gauss[x_] := Evaluate@PDF[NormalDistribution[], x];

With[{amp1 = 1.2, amp2 = 0.5, width1 = 1, width2 = 2, sharedOffset = 0.5, eps = 0.05},
  dat1 = Table[{x, amp1 gauss[(x - sharedOffset)/width1] + eps RandomVariate[NormalDistribution[]]}, {x, xvals}];
  dat2 = Table[{x, amp2 gauss[(x - sharedOffset)/width2] + eps RandomVariate[NormalDistribution[]]}, {x, xvals}]
  ];
plot = ListPlot[{dat1, dat2}]
Out[6]=

Fit with the models that were used to generate the data:

In[7]:=
fit = ResourceFunction["MultiNonlinearModelFit"][
  {dat1, dat2},
  {
   amp1 gauss[(x - sharedOffset)/width1],
   amp2 gauss[(x - sharedOffset)/width2]
   },
  {amp1, amp2, width1, width2, sharedOffset},
  {x}
  ]
Out[7]=
In[8]:=
fit["BestFitParameters"]
Out[8]=

Extract the fits as a list of expressions:

In[9]:=
fits = Table[Normal[fit], {\[FormalN], {1, 2}}]
Out[9]=

Compare the fits to the data:

In[10]:=
Show[
 plot,
 Plot[fits, {x, -5, 5}]
 ]
Out[10]=

Options

Weights

The Weights option can be specified in a number of ways:

First generate datasets with an unequal number of points and offset them slightly:

In[11]:=
gauss[x_] := Evaluate@PDF[NormalDistribution[], x];
With[{amp1 = 1.2, amp2 = 0.5, width1 = 1, width2 = 2, sharedOffset = 0.5, eps = 0.025},
  dat1 = Table[
    {x, amp1 gauss[(x - sharedOffset)/width1] + eps RandomVariate[NormalDistribution[]]},
    {x, -5, 5, 0.1}
    ];
  dat2 = Table[
    {x, amp2 gauss[(x - sharedOffset + 1)/width2] + eps RandomVariate[NormalDistribution[]]},
    {x, -5, 5, 0.2}
    ]
  ];
fitfuns = {
   amp1 gauss[(x - sharedOffset)/width1],
   amp2 gauss[(x - sharedOffset)/width2]
   };
fitParams = {amp1, amp2, width1, width2, sharedOffset};
variables = {x};
plot = ListPlot[{dat1, dat2}]
Out[16]=

Fit the data normally. In this case, each individual data point has equal weights in the fit, so the first dataset gets more weight overall since it has more points:

In[17]:=
fit1 = ResourceFunction["MultiNonlinearModelFit"][
   {dat1, dat2},
   fitfuns, fitParams, variables
   ];
fit1["BestFitParameters"]
Out[18]=
In[19]:=
Show[plot, Plot[Evaluate@Table[Normal[fit1], {\[FormalN], {1, 2}}], {x, -5, 5}]]
Out[19]=

Assign more weight to the second dataset:

In[20]:=
fit2 = ResourceFunction["MultiNonlinearModelFit"][
   {dat1, dat2},
   fitfuns, fitParams, variables,
   Weights -> {1, 20}
   ];
fit2["BestFitParameters"]
Out[21]=
In[22]:=
Show[plot, Plot[Evaluate@Table[Normal[fit2], {\[FormalN], {1, 2}}], {x, -5, 5}]]
Out[22]=

Assign weights inversely proportional to the number of points in the dataset. This asserts that each dataset is equally important:

In[23]:=
fit3 = ResourceFunction["MultiNonlinearModelFit"][
   {dat1, dat2},
   fitfuns, fitParams, variables,
   Weights -> "InverseLengthWeights"
   ];
fit3["BestFitParameters"]
Out[24]=
In[25]:=
Show[plot, Plot[Evaluate@Table[Normal[fit3], {\[FormalN], {1, 2}}], {x, -5, 5}]]
Out[25]=

To assign weights for each individual data point, you can pass a list of vectors matching the input data:

In[26]:=
fit4 = ResourceFunction["MultiNonlinearModelFit"][
   {dat1, dat2},
   fitfuns, fitParams, variables,
   Weights -> {
     RandomReal[{0, 1}, Length[dat1]],
     RandomReal[{0, 1}, Length[dat2]]
     }
   ];
fit4["BestFitParameters"]
Out[27]=
In[28]:=
Show[plot, Plot[Evaluate@Table[Normal[fit4], {\[FormalN], {1, 2}}], {x, -5, 5}]]
Out[28]=

“DatasetIndexSymbol”

Use a different symbol to index the datasets:

In[29]:=
fit = ResourceFunction["MultiNonlinearModelFit"][
   RandomVariate[BinormalDistribution[0.7], {2, 100}],
    {a x + b1, a x + b2}, {a, b1, b2}, {x}, "DatasetIndexSymbol" -> mySymbol];
Normal[fit]
Out[30]=
In[31]:=
Table[Normal[fit], {mySymbol, {1, 2}}]
Out[31]=

Resource History

License Information