1

I have time varying data trace which I want to fit a function to. The inputs to the functions are lists and I want the curve_fit to optimize all values in the list to fit the curve. I have gotten so far-

from scipy.optimize import curve_fit
from matplotlib.pylab import plt
from numpy import exp

def ffunc2(x, a, b):
    counter = 0
    return_value = 0
    while counter < len(a):
        return_value += a[counter] * exp(b[counter] * x)
        counter += 1
    return return_value

# INITIAL DATA
x = [1, 2, 3, 5]
y = [1, 8, 81, 125]


number_variable = 2

# INTIAL GUESS
p0 = []
counter = 0
while counter < number_variable:
    p0.append(0.0)
    counter += 1

p, _ = curve_fit(ffunc2, x, y, p0=[0.0, 0.0])

I want to create a loop which iterates such that it gives me the best fit with maximum number of variables by minimizing the error.

I have found this discussion as well - Using scipy curve_fit for a variable number of parameters

from numpy import exp
from scipy.optimize import curve_fit

def wrapper_fit_func(x, N, *args):
    a, b, c = list(args[0][:N]), list(args[0][N:2*N]), list(args[0][2*N:3*N])
    return fit_func(x, a, b)

def fit_func(x, a, b):
    counter = 0
    return_value = 0
    while counter < len(a):
        return_value += a[counter] * exp(b[counter] * x)
        counter += 1
    return return_value


x = [1, 2, 3, 5]
y = [1, 8, 81, 125]
params_0 = [0,1.0,2.0,3.0,4.0,5.0]

popt, pcov = curve_fit(lambda x, *params_0: wrapper_fit_func(x, 3, params_0), x, y, p0=params_0)

But get an error -´´´ File "C:\python\lib\site-packages\scipy\optimize\minpack.py", line 387, in leastsq raise TypeError('Improper input: N=%s must not exceed M=%s' % (n, m)) TypeError: Improper input: N=6 must not exceed M=4 ´´´

codingtoddler
  • 41
  • 1
  • 6

3 Answers3

2

I was able to solve this be using sci_py.optimize.least_squares directly as it takes in tuple as an input and not variables directly. But I do have to define the error function. I would assume it help solve my problem as of now.

from scipy.optimize import least_squares
from matplotlib.pylab import plt
from numpy import exp
import numpy as np


# define function to fit
def ffunc2(a, x):
    counter = 0
    return_value = 0
    while counter < len(a):
        return_value += a[counter] * exp(x * a[counter + 1])
        counter += 2

    return return_value


def error_func(tpl, x, y):
    return ffunc2(tpl,x) - y


# INPUT DATA
x = np.array([1, 2, 3, 5])
y = np.array([0.22103418, 0.24428055, 0.26997176, 0.32974425,])

# INITIAL GUESS
p0 = (1, 1)*10
output = least_squares(error_func, x0=p0, jac='2-point', bounds=(0, np.inf), method='trf', ftol=1e-08,
                       xtol=1e-08, gtol=1e-08, x_scale=1.0, loss='linear', f_scale=1.0, diff_step=None,
                       tr_solver=None,
                       tr_options={}, jac_sparsity=None, max_nfev=None, verbose=0, args=(x, y))

tpl_final = output.x
print (tpl_final)

final_curve = ffunc2(tpl_final,x)

plt.plot(x, y, 'r-', x, final_curve, 'g-')
plt.show()

codingtoddler
  • 41
  • 1
  • 6
0

The short answer is "No". curve_fit requires a single list/ndarray of variable parameters that correspond, in order, with the arguments of the model function you provide. That is, you would have to explicitly name all the parameters in your function and make your list of lists of variables strictly 1D.

Then again, curve_fit is not much more than a wrapper around scipy.optimize.least_squares. To use that approach, you pass in a (still strictly 1D) list/ndarray and use those values to build the array to be minimized (typically data-model) from that single array of variables. In some complex cases with many components or data sets, that can become easier to use. That is, the curve_fit approach doesn't scale very well to 50 positional variables.

Depending on the nature of your problem, you might also find lmfit (https://lmfit.github.io/lmfit-py/ -- disclaimer: I am the original author) useful. This organizes Parameters by name, not position in a list and provides more built-in ways to constrain Parameters and explore uncertainties. What may be of particular for your case, the lmfit.Model class for curve-fitting includes the ability to easily add models together into a composite (for example, 2 Gaussians + n exponential background as shown at https://lmfit.github.io/lmfit-py/examples/documentation/builtinmodels_nistgauss2.html). That might help you express what you are trying to do.

M Newville
  • 7,486
  • 2
  • 16
  • 29
0

Please take a look at this post https://stackoverflow.com/a/73951825/20160627, where I propose a use of scipy.optimize.curve_fit with arbitrary number and positioning of parameters to fit or fix in a list