Search code examples
pythonnumpycurve-fittingscipy-optimize

Failing to pass a tuple of initial parameters into scipy.optimize curve_fit


I want to fit a sum of a polynomial and a sine to a data set. I do not manage to pass a set of initial values for the coefficients (initial guess) into the function. I know that the asterisk (*) packs the variable.

I define the function

import pandas as pd
import numpy as np
from numpy.polynomial import Polynomial as P

def func(x,*A):
    A=A[0]  # to get the values only
    polynom=P(A[0:-3]) # all but the last three coefficients go into the polynomial
    sine=A[-3]*np.sin(A[-2]*(x-A[-1])) # the last three coefficients are for the sine
    y=polynom(x)+sine # sum up both
    return y

Let's take some test values:

P0=(1,2,3,4,5,6,7,8,9,10,11)

The call of curve_fit fails:

coefs_scipy,pcov = sio.curve_fit(func,df.length,df.s,p0=P0)

where df is a pandas dataframe, the columns length and s containing the x and y values respectively.

The last error is

\AppData\Local\Temp/ipykernel_16648/911748954.py in func(x, *A)
     29 def func(x,*A):
     30     A=A[0]  # to get the values only
---> 31     polynom=P(A[0:-3]) # all but the last three coefficients go into the polynomial
     32     sine=A[-3]*np.sin(A[-2]*(x-A[-1])) # the last three coefficients are for the sine
     33     y=polynom(x)+sine # sum up both

IndexError: invalid index to scalar variable.

indicating that it is not possible to extract [0:-3] from a scalar. Though A is not a scalar I assume

Astonishingly, when I just call the function with func(1,P0) it works.

What is wrong?


Solution

  • The curve_fit function internally calls the target function like

    func(x, *P0)
    

    That means all the values in the P0 tuple are expanded into positional arguments to the function.

    The function declaration

    def func(x, *A):
        ...
    

    collects all these positional arguments into A. So A is a tuple containing all the values and not a tuple of tuples.

    To solve your problem, you should remove the

    A=A[0]
    

    line and change the direct call to the function to

    func(1, *P0)