Search code examples
python-3.xlmfitscipy-optimize

Python3, scipy.optimize: Fit model to multiple datas sets


I have a model which is defined as:

m(x,z) = C1*x^2*sin(z)+C2*x^3*cos(z)

I have multiple data sets for different z (z=1, z=2, z=3), in which they give me m(x,z) as a function of x.

The parameters C1 and C2 have to be the same for all z values.

So I have to fit my model to the three data sets simultaneously otherwise I will have different values of C1 and C2 for different values of z.

It this possible to do with scipy.optimize. I can do it for just one value of z, but can't figure out how to do it for all z's.

For one z I just write this:

def my_function(x,C1,C1):
    z=1
return C1*x**2*np.sin(z)+ C2*x**3*np.cos(z)


data = 'some/path/for/data/z=1'

x= data[:,0]
y= data[:,1]

from lmfit import Model

gmodel = Model(my_function)
result = gmodel.fit(y, x=x, C1=1.1)

print(result.fit_report())

How can I do it for multiple set of datas (i.e different z values?)


Solution

  • So what you want to do is fit a multi-dimensional fit (2-D in your case) to your data; that way for the entire data set you get a single set of C parameters that bests describes your data. I think the best way to do this is using scipy.optimize.curve_fit().

    So your code would look something like this:

    import scipy.optimize as optimize
    import numpy as np
    
    def my_function(xz, *par):
        """ Here xz is a 2D array, so in the form [x, z] using your variables, and *par is an array of arguments (C1, C2) in your case """
        x = xz[:,0]
        z = xz[:,1]
        return par[0] * x**2 * np.sin(z) + par[1] * x**3 * np.cos(z)
    
    # generate fake data. You will presumable have this already
    x = np.linspace(0, 10, 100)
    z = np.linspace(0, 3, 100)
    xx, zz = np.meshgrid(x, z) 
    xz = np.array([xx.flatten(), zz.flatten()]).T
    fakeDataCoefficients = [4, 6.5]
    fakeData = my_function(xz, *fakeDataCoefficients) + np.random.uniform(-0.5, 0.5, xx.size)
    
    # Fit the fake data and return the set of coefficients that jointly fit the x and z
    # points (and will hopefully be the same as the fakeDataCoefficients
    popt, _ = optimize.curve_fit(my_function, xz, fakeData, p0=fakeDataCoefficients)
    
    # Print the results
    print(popt)
    

    When I do this fit I get precisely the fakeDataCoefficients I used to generate the function, so the fit works well.

    So the conclusion is that you don't do 3 fits independently, setting the value of z each time, but instead you do a 2D fit which takes the values of x and z simultaneously to find the best coefficients.