1

I have a relatively complex plot which I would like to model. The model consists of two 'phases'/components which have been measured experimentally, however the ratio of these components is unknown. The data is shown in the image: where some combination of the orange and green curves make up the blue curve.

Complex curve and two components

The model is defined as:

    Model = A * (z1 * 0.3 * y1 + z2 * 0.7 * y2) + D

where A is an overall scaling, z1 and z2 are the individual scalings for y1 and y2 which are the components. D is an offset.

I have tried to use the lmfit minimise function by defining the fit parameters and residual like so:

import lmfit
from lmfit import Minimizer, Parameters, report_fit

fit_params = Parameters()
fit_params.add('A', value=1.00, min=0, max=1)
fit_params.add('z1', value=1, min=0, max=1)
fit_params.add('z2', value=1, min=0, max=1)
fit_params.add('D', value=0.00, min=-5, max=5)

def residual(pars, x, data = total_scatter_expt):
   Data = total_scatter_expt
   Model = A * (z1 * 0.3 * y1 + z2 * 0.7 * y2) + D
   return model - data

Because y1 and y2 are also functions it's no entirely clear to me what my x is in the residual or in the minimisation line. For this I have used the x values on the graph in the picture.

out = minimize(residual, fit_params, args=(x,))     
print(fit_report(out))

This has led to the error

TypeError: can't multiply sequence by non-int of type 'float'

I'm not sure whether this problem would be best described as a deconvolution or perhaps fitting functions of functions. Any help would be appreciated.

  • Do I get it right...`z1` and `z2` are scaling functions? so you need to get values over the full data range? In that case you do not need `A` and still your system is completely under-determined. If you can assume a certain smoothness you could try with a nth order polynomial or spline. – mikuszefski May 20 '20 at 06:22
  • Yes, that is correct. z1 and z2 are scaling functions. – JoshuaT31524 May 20 '20 at 07:41

1 Answers1

1

You claim that your objective function is:

def residual(pars, x, data = total_scatter_expt):
   Data = total_scatter_expt
   Model = A * (z1 * 0.3 * y1 + z2 * 0.7 * y2) + D
   return model - data

There a number of problems here:

  1. 'data = total_scatter_expt' means the default value for data will be the value of total_scatter_expt at the time of function definition (like when the text of this code is processed). Don't do that. Anyway, total_scatter_expt is not defined. This cannot be the code you actually ran.

  2. You then set Data to total_scatter_expt. Again, this cannot be valid code.

  3. You capitalize both Data and Model but then return model-data. Python is case sensitive so your code cannot be valid.

  4. You access A, z1, y1, z2, y2, and D which are not defined and never use pars or x. Again, your code cannot be valid.

Almost certainly you want to extract the values for the parameters A, D, etc from pars, but you don't do this. Perhaps try

   pvals = pars.valuesdict()
   model = pvals['A'] * (pvals['z1'] ....

Probably you mean x to encompass y1 and/or y2.

In the future, give the code that actually shows the problem you are actually having. Do not lie about the error message you get: show the actual messages you get with the actual code you post.

M Newville
  • 7,486
  • 2
  • 16
  • 29
  • true...but: while one could argue that there is no proper question...this is not an answer. – mikuszefski May 25 '20 at 08:56
  • um, wait, "one could argue" that there is no proper question in every situation. Not only is this not an answer, "one could argue" that there is no answer without a question, and there is no question at all. There is no spoon. And this is not a comment. ;) – M Newville May 25 '20 at 12:58
  • I did not say this. I am not here. :) – mikuszefski May 25 '20 at 13:28