Search code examples
pythontypeerrorcurve-fittingscipy-optimize

Why do I get type error when using scipy.optimize.curve_fit to do a least squares fit test?


I'm trying to fit a function using curve_fit from the scipy.optimize module. I keep getting the error message:

"TypeError: only size-1 arrays can be converted to Python scalars'.

frequency = []
for i in driven_period:
    frequency.append(2*math.pi*i**-1)
   
amplitude = []
for i in driven_height:
    amplitude.append(i-41.3)
   
def amplitude_func(omega, p, omega0, gamma):
    x=((p*omega0**2)/(math.sqrt((omega0**2-omega**2)**2+gamma**2*omega**2)))
    return x

parameters = sp.curve_fit(amplitude_func, frequency, amplitude)
p_fit = parameters[0][0]
omega0_fit = parameters[0][1]
gamma_fit = parameters[0][2]

fit=[]
for i in frequency:
    fit.append(amplitude_func(i, p_fit, w_fit, y_fit))

plt.scatter(frequency, amplitude)
plt.plot(frequency, fit)

I've tried this same code with a basic linear function and it worked, so I think the problem must be with the function amplitude_func, but I can't figure out what it is. I've tried printing at every stage, but I cant find where I have the mistake. Sorry if this is a silly question, I'm new to programming. Help appreciated.

Edit: more info. Error message:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Input In [151], in <cell line: 27>()
     24     return (p*(omega)**2)/(math.sqrt((omega0)**2-(omega)**2)**2+(gamma)**2*(omega**2))
     26 # lorenzian least-squares fit
---> 27 amplitude_fit, amplitude_covariance = sp.curve_fit(amplitude_func, frequency, amplitude)
     28 p_fit = amplitude_fit[0]
     29 omega0_fit = amplitude_fit[1]

File ~/opt/anaconda3/lib/python3.9/site-packages/scipy/optimize/minpack.py:789, in curve_fit(f, xdata, ydata, p0, sigma, absolute_sigma, check_finite, bounds, method, jac, **kwargs)
    787 # Remove full_output from kwargs, otherwise we're passing it in twice.
    788 return_full = kwargs.pop('full_output', False)
--> 789 res = leastsq(func, p0, Dfun=jac, full_output=1, **kwargs)
    790 popt, pcov, infodict, errmsg, ier = res
    791 ysize = len(infodict['fvec'])

File ~/opt/anaconda3/lib/python3.9/site-packages/scipy/optimize/minpack.py:410, in leastsq(func, x0, args, Dfun, full_output, col_deriv, ftol, xtol, gtol, maxfev, epsfcn, factor, diag)
    408 if not isinstance(args, tuple):
    409     args = (args,)
--> 410 shape, dtype = _check_func('leastsq', 'func', func, x0, args, n)
    411 m = shape[0]
    413 if n > m:

File ~/opt/anaconda3/lib/python3.9/site-packages/scipy/optimize/minpack.py:24, in _check_func(checker, argname, thefunc, x0, args, numinputs, output_shape)
     22 def _check_func(checker, argname, thefunc, x0, args, numinputs,
     23                 output_shape=None):
---> 24     res = atleast_1d(thefunc(*((x0[:numinputs],) + args)))
     25     if (output_shape is not None) and (shape(res) != output_shape):
     26         if (output_shape[0] != 1):

File ~/opt/anaconda3/lib/python3.9/site-packages/scipy/optimize/minpack.py:485, in _wrap_func.<locals>.func_wrapped(params)
    484 def func_wrapped(params):
--> 485     return func(xdata, *params) - ydata

Input In [151], in amplitude_func(omega, p, omega0, gamma)
     23 def amplitude_func(omega, p, omega0, gamma):
---> 24     return (p*(omega)**2)/(math.sqrt((omega0)**2-(omega)**2)**2+(gamma)**2*(omega**2))

TypeError: only size-1 arrays can be converted to Python scalars

Arrays:

driven_period = [1.027, 0.949, 0.866, 0.806, 0.648, 0.917, 0.935, 1.404, 1.113, 0.887, 0.983]
driven_height = [43.1, 45.35, 43.7, 42.25, 41.3, 45.9, 45.7, 41.5, 42.25, 44.75, 44.0]

Solution

  • I think I figuered it out:

    Python seems to have trouble multiplying arrays ( ** operator as well as math library). Switching over to np for calculations works. Sadly I can’t explain in depth why :/

    import math
    import numpy as np
    
    driven_period = [1.027, 0.949, 0.866, 0.806, 0.648, 0.917, 0.935, 1.404, 1.113, 0.887, 0.983]
    driven_height = [43.1, 45.35, 43.7, 42.25, 41.3, 45.9, 45.7, 41.5, 42.25, 44.75, 44.0]
    
    frequency = []
    for i in driven_period:
        frequency.append(2*math.pi*i**-1)
    
    amplitude = []
    for i in driven_height:
        amplitude.append(i-41.3)
    
    def amplitude_func(omega, p, omega0, gamma):
        #x=((p*omega0**2)/(math.sqrt((omega0**2-omega**2)**2+gamma**2*omega**2)))
        p1 = np.power(omega0,2)-np.power(omega,2)
        p2 = np.power(gamma*omega,2)
        return p*np.power(omega0,2) / (np.sqrt(p1+p2))
        
    parameters = sp.curve_fit(amplitude_func, frequency, amplitude)
    p_fit = parameters[0][0]
    omega0_fit = parameters[0][1]
    gamma_fit = parameters[0][2]
    
    print(parameters)
    
    fit=[]
    for i in frequency:
        fit.append(amplitude_func(i, p_fit, omega0_fit, gamma_fit))
    

    Right now I’m only having access to Python interpreter via ssh. So I can not ensure whether the plot and fit make sense or not. Splitting the function to vars p1 and p2 was only for debugging purposes. It could be merged back together.