Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass tuple as input argument for scipy.optimize.curve_fit

I have the following code:

import numpy as np
from scipy.optimize import curve_fit


def func(x, p): return p[0] + p[1] + x


popt, pcov = curve_fit(func, np.arange(10), np.arange(10), p0=(0, 0)) 

It will raise TypeError: func() takes exactly 2 arguments (3 given). Well, that sounds fair - curve_fit unpact the (0, 0) to be two scalar inputs. So I tried this:

popt, pcov = curve_fit(func, np.arange(10), np.arange(10), p0=((0, 0),))

Again, it said: ValueError: object too deep for desired array

If I left it as default (not specifying p0):

popt, pcov = curve_fit(func, np.arange(10), np.arange(10))  

It will raise IndexError: invalid index to scalar variable. Obviously, it only gave the function a scalar for p.

I can make def func(x, p1, p2): return p1 + p2 + x to get it working, but with more complicated situations the code is going to look verbose and messy. I'd really love it if there's a cleaner solution to this problem.

Thanks!

like image 994
Yuxiang Wang Avatar asked Aug 20 '13 03:08

Yuxiang Wang


3 Answers

Not sure if this is cleaner, but at least it is easier now to add more parameters to the fitting function. Maybe one could even make an even better solution out of this.

import numpy as np
from scipy.optimize import curve_fit


def func(x, p): return p[0] + p[1] * x

def func2(*args):
    return func(args[0],args[1:])

popt, pcov = curve_fit(func2, np.arange(10), np.arange(10), p0=(0, 0))
print popt,pcov

EDIT: This works for me

import numpy as np
from scipy.optimize import curve_fit

def func(x, *p): return p[0] + p[1] * x

popt, pcov = curve_fit(func, np.arange(10), np.arange(10), p0=(0, 0))
print popt,pcov
like image 99
cass Avatar answered Oct 25 '22 07:10

cass


Problem

When using curve_fit you must explicitly say the number of fit parameters. Doing something like:

def f(x, *p):
    return sum( [p[i]*x**i for i in range(len(p))] )

would be great, since it would be a general nth-order polynomial fitting function, but unfortunately, in my SciPy 0.12.0, it raises:

ValueError: Unable to determine number of fit parameters.

Solution

So you should do:

def f_1(x, p0, p1):
    return p0 + p1*x

def f_2(x, p0, p1, p2):
    return p0 + p1*x + p2*x**2

and so forth...

Then you can call using the p0 argument:

curve_fit(f_1, xdata, ydata, p0=(0,0))
like image 45
Saullo G. P. Castro Avatar answered Oct 25 '22 07:10

Saullo G. P. Castro


scipy.optimize.curve_fit

scipy.optimize.curve_fit(f, xdata, ydata, p0=None, sigma=None, **kw)

Use non-linear least squares to fit a function, f, to data.

Assumes ydata = f(xdata, *params) + eps

Explaining the idea

The function to be fitted should take only scalars (not: *p0). Remember that the result of the fit depends on the initialization parameters.

Working example

import numpy as np
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt

def func(x, a0, a1):
    return a0 + a1 * x

x, y = np.arange(10), np.arange(10) + np.random.randn(10)/10
popt, pcov = curve_fit(func, x, y, p0=(1, 1))

# Plot the results
plt.title('Fit parameters:\n a0=%.2e a1=%.2e' % (popt[0], popt[1]))
# Data
plt.plot(x, y, 'rx')
# Fitted function
x_fine = np.linspace(x[0], x[-1], 100)
plt.plot(x_fine, func(x_fine, popt[0], popt[1]), 'b-')
plt.savefig('Linear_fit.png')
plt.show()

Result from the fit is shown in the plot.

like image 37
strpeter Avatar answered Oct 25 '22 09:10

strpeter