3

I am trying to fit a function which takes as input 2 independent variables x,y and 3 parameters to be found a,b,c. This is my test code:

import numpy as np
from scipy.optimize import curve_fit

def func(x,y, a, b, c):
    return a*np.exp(-b*(x+y)) + c    

y= x = np.linspace(0,4,50)
z = func(x,y, 2.5, 1.3, 0.5) #works ok
#generate data to be fitted
zn = z + 0.2*np.random.normal(size=len(x))
popt, pcov = curve_fit(func, x,y, zn) #<--------Problem here!!!!!

But i am getting the error: "func() takes exactly 5 arguments (51 given)". How can pass my arguments x,y correctly?

elyase
  • 39,479
  • 12
  • 112
  • 119
  • What does the documentation for `curve_fit` say about the `func` parameter? Obviously it is trying to call it with 51 parameters, so maybe you should re-read the documentation. – Wes Apr 15 '12 at 17:06

2 Answers2

7

A look at the documentation of scipy.optimize.curve_fit() is all it takes. The prototype is

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

The documentation states curve_fit() is called with the target function as the first argument, the independent variable(s) as the second argument, the dependent variable as the third argument ans the start values for the parameters as the forth argument. You tried to call the function in a completely different way, so it's not surprising it does not work. Specifically, you passed zn as the p0 parameter – this is why the function was called with so many parameters.

The documentation also describes how the target function is called:

f: callable
The model function, f(x, ...). It must take the independent variable as the first argument and the parameters to fit as separate remaining arguments.

xdata : An N-length sequence or an (k,N)-shaped array
for functions with k predictors. The independent variable where the data is measured.

You try to uses to separate arguments for the dependent variables, while it should be a single array of arguments. Here's the code fixed:

def func(x, a, b, c):
    return a * np.exp(-b * (x[0] + x[1])) + c    

N = 50
x = np.linspace(0,4,50)
x = numpy.array([x, x])          # Combine your `x` and `y` to a single
                                 # (2, N)-array
z = func(x, 2.5, 1.3, 0.5)
zn = z + 0.2 * np.random.normal(size=x.shape[1])
popt, pcov = curve_fit(func, x, zn)
Community
  • 1
  • 1
Sven Marnach
  • 574,206
  • 118
  • 941
  • 841
0

Try to pass the first two array parameters to func as a tuple and modify func to accept a tuple of parameters

Normally it is expected the curvefit would accept an x and y parameter func(x) as an input to fit the curve. Strangely in your example as your x parameter is not a single value but two values (not sure why), you have to modify your function so that it accept the x as a single parameter and expands it within.

Generally speaking, three dimensional curve fitting should be handled in a different manner from what you are trying to achieve. You can take a look into the following SO post which tried to fit a three dimensional scatter with a line.

>>> def func((x,y), a, b, c):
    return a*np.exp(-b*(x+y)) + c

>>> y= x = np.linspace(0,4,50)
>>> z = func((x,y), 2.5, 1.3, 0.5) #works ok
>>> zn = z + 0.2*np.random.normal(size=len(x))
>>> popt, pcov = curve_fit(func, (x,y), zn)
Community
  • 1
  • 1
Abhijit
  • 62,056
  • 18
  • 131
  • 204