Search code examples
pythoncurve-fittingscipy-optimize

scipy.optimize.curve_fit - TypeError: only size-1 arrays can be converted to Python scalars


I know the topic was already tackled 6 years ago but either I don't really understand to solution proposed or it does not work for me... I am trying to fit a small serie of data with a sigmoide curve with curve_fit() from scipy.optimize. I have the following script :

CO = [336.6,249.7,154.2,117.7,36.8,53.4]
COp = [434.9,406,290.7,86.2,66.0,83.5]

cycle_lst = [1,2,3,4,5,6]

def fit_func(x,a,b):
    return (a/(1+exp(-b*x)))

popt_res,popc_res = curve_fit(fit_func, xdata = cycle_lst, ydata = CO)


abs_gen = np.linspace(1,6,1000)
sim_fit = []
for i in abs_gen:
    print(fit_func(popt_res[0],popt_res[1],i))
    sim_fit.append(fit_func(popt_res[0],popt_res[1],i))

plt.scatter(cycle_lst,COp, label = "Digestion with polyphosphate")
plt.plot(abs_gen,sim_fit,label = "modeled curve")
plt.xlabel("Number of cycle performed")
plt.ylabel("Remaining mass (g)")
plt.legend()
plt.grid(True, axis = 'both')
plt.show()

Which raises the error :

  File "<ipython-input-80-6d6b9f70c774>", line 2, in fit_func
    return (a/(1+exp(-b*x)))

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

What I can't really understand is that, right before, I was able to fit my data without modifying my list or trying to pass the value in the function one by one. In the curve_fit() documentation nothing is specified about the fact that the function cannot proceeds list or array (and if it does not I can't really understand why as the parameters of the sigmoide should be determined with a serie of values). My mistake is probably really dumb but if someone could help it would really save my mental health, I know python a bit but I am definitely not an expert.


Solution

  • The problem lies within the function that you want scipy to optimize:

    def fit_func(x, a: float, b: float):
        return a / (1 + exp(-x * b))
    

    This causes problems, since the exp() from the math module seems to only support single values. A quick way to circumvent this, is to use the exp() function from the numpy module instead, which supports operations on lists.

    def fit_func(x, a: float, b: float):
        return a / (1 + np.exp(-x * b))