Scipy's
curve_fit()
uses iterations to search for optimal parameters. If the number of iterations exceeds the default number of 800, but the optimal parameters are still not found, then this error will be raised.
Optimal parameters not found: Number of calls to function has reached maxfev = 800
You can provide some initial guess parameters for curve_fit(), then try again. Or, you can increase the allowable iterations. Or do both!
Here is an example:
popt, pcov = curve_fit(exponenial_func, x, y, p0=[1,0,1], maxfev=5000)
p0 is the guess
maxfev is the max number of iterations
You can also try setting bounds which will help the function find the solution. However, you cannot set bounds and a max_nfev at the same time.
popt, pcov = curve_fit(exponenial_func, x, y, p0=[1,0,1], bounds=(1,3))
Source1: https://github.com/scipy/scipy/issues/6340
Source2: My own testing and finding that the about github is not 100% accurate
Also, other recommendations about not using 0 as an 'x' value are great recommendations. Start your 'x' array with 1 to avoid divide by zero errors.
Answer from embulldogs99 on Stack Overflowpython - Why does scipy.optimize.curve_fit not fit correctly to the data? - Stack Overflow
Fitting using curve_fit of scipy in python gives totally different answer for 1/t and t - Computational Science Stack Exchange
curve fitting - What does 'maxfev' do in iPython Notebook? - Stack Overflow
Handle scipy fitting errors in `xarray.curve_fit`
Scipy's
curve_fit()
uses iterations to search for optimal parameters. If the number of iterations exceeds the default number of 800, but the optimal parameters are still not found, then this error will be raised.
Optimal parameters not found: Number of calls to function has reached maxfev = 800
You can provide some initial guess parameters for curve_fit(), then try again. Or, you can increase the allowable iterations. Or do both!
Here is an example:
popt, pcov = curve_fit(exponenial_func, x, y, p0=[1,0,1], maxfev=5000)
p0 is the guess
maxfev is the max number of iterations
You can also try setting bounds which will help the function find the solution. However, you cannot set bounds and a max_nfev at the same time.
popt, pcov = curve_fit(exponenial_func, x, y, p0=[1,0,1], bounds=(1,3))
Source1: https://github.com/scipy/scipy/issues/6340
Source2: My own testing and finding that the about github is not 100% accurate
Also, other recommendations about not using 0 as an 'x' value are great recommendations. Start your 'x' array with 1 to avoid divide by zero errors.
Your original data is t1 and F1. Therefore curve_fit should be given t1 as its second argument, not t.
popt, pcov = curve_fit(func, t1, F1, maxfev=1000)
Now once you obtain fitted parameters, popt, you can evaluate func at the points in t to obtain a fitted curve:
t = np.linspace(1, 3600 * 24 * 28, 13)
plt.plot(t, func(t, *popt), label="Fitted Curve")
(I removed zero from t (per StuGrey's answer) to avoid the Warning: divide by zero encountered in log.)
import matplotlib.pyplot as plt
import scipy.optimize as optimize
import numpy as np
# data
F1 = np.array([
735.0, 696.0, 690.0, 683.0, 680.0, 678.0, 679.0, 675.0, 671.0, 669.0, 668.0,
664.0, 664.0])
t1 = np.array([
1, 90000.0, 178200.0, 421200.0, 505800.0, 592200.0, 768600.0, 1036800.0,
1371600.0, 1630800.0, 1715400.0, 2345400.0, 2409012.0])
plt.plot(t1, F1, 'ro', label="original data")
# curvefit
def func(t, a, b):
return a + b * np.log(t)
popt, pcov = optimize.curve_fit(func, t1, F1, maxfev=1000)
t = np.linspace(1, 3600 * 24 * 28, 13)
plt.plot(t, func(t, *popt), label="Fitted Curve")
plt.legend(loc='upper left')
plt.show()

Firstly try not to increase maxfev so large, this is usually a sign something else is going wrong! Playing around I can get a fit by the following addition:
def f(x, b, a, k):
return (b/(np.sqrt(1 + a*((k-x)**2))))
popt, pcov = curve_fit(f, x, y, p0=[20, 600.0, 35.0])
Firstly give the fitting function you have given has a maximum of 1, since the peak in your data is 600, it will never fit. So I added an overall factor b. Secondly , try to help poor old curve_fit out. If by eye you can see it peaks at x~35 then tell it through the p0. This requires some intuition as to how the function works but is very important if your going to use a curve fitting function.

I looked at the raw data on an X-Y scatterplot, an equation to fit this data appears to require a very sharp, narrow peak. The equation you have been given will not yield a peak response. In my opinion, a fit of this data to the given equation won't work for this reason.
--- SOLVED ----
u/Standardw and u/kra_pao provided me with the answers: Passing an array-like initial set of parameters to curve_fit helps, i.e.:
# define start values for scalar parameters (so that len(args) is known) p = [1] * len(expList) # fit data to model popt, pcov = curve_fit(polymodel, xSamples, valSamps, p0 = p, maxfev = 10000)
However, with this, there is a warning message from curve_fit. This new problem seems to originate in my polynomial model. I will work on that and of course post the solution here.
UPDATE:
Fixed it completely and posted resulting code in my comment below.
Original Post:
Hi r/learnpython!
This subreddit has really helped me a lot and continues to do so when I'm trying to code something in python. Thank you all for your great posts!
Now I turn to you for help.
Context
I'm working on my thesis (mechanical engineering, so I don't know a lot about computers or programming). In a nutshell, I'm trying out different ways to optimize the shape of a mechanical structure and compare multiple optimization techniques. Python is the language to go to for that because it's easily automatable, can directly start my FEM programs with alternative parameters and so on. The code will not be published.
My Specific Problem
I have some sample data that I want to fit to a polynomial model. What I'm too stupid for: I can't figure out how to "create the model" during running the program so that it fits my requirements (i.e. number of dimensions and maximum degree). I tried to boil it down a bit, so as an example I use the ackley function here.
So I have sample data (x, y and function values) of the ackley function and want to fit a model to it. (This level of inaccuaracy is fine - I just need the minimum or can worry about the accuaracy later)
When I use the function also as a model, it works. That's how I got the model in the second picture. When I try to dynamically build the model (sorry, I don't know how to express it better), the code fails:
ValueError: Unable to determine number of fit parameters.
This makes sense: I don't specify the number of *args. I do understand that. But how do I fix it?
The Example Code
Remarks for the functions are below the code block. My problem part is set within "###############" above and below. It's at "# fit data to model".As you can see, I try to define my polynomial model on the run. Doesn't seem elegant to me, I just kind of feel helpless here.
import numpy as np
from itertools import product
from scipy.optimize import curve_fit
def myfunc(x, a=20, b=0.2, c=2*np.pi):
# ackley function
d = len(x)
sumOne = sum([xi**2 for xi in x])
sumTwo = sum([np.cos(c*xi) for xi in x])
val = -a * np.exp(-b * np.sqrt(1/d * sumOne)) - np.exp( 1/d * sumTwo) + a + np.exp(1)
return val
def getpolyexponents(nDim, kDegree):
"""gets the basis function exponents for a polynom"""
# repeat range {0,...,kDegree} nDim times as rangeSet
rangeSet = [range(0, kDegree+1) for i in range(0, nDim)]
# create full product set
expList = [item for item in product(*rangeSet)]
# keep only items in list with correct degree,
# i.e. sum(exponents) <= kDegree
expList[:] = [item for item in expList if sum(item) <= kDegree]
# return the polynom exponents
return expList
def getbasisfunc(x, exponentSet):
"""gets a single basis function for a polynom with an exponent set"""
# make list of single elements of basis function, e.g. x^0*y^0, x^0*y^1, ...
productElements = [x[i]**exponentSet[i] for i in range(0,len(exponentSet))]
# calculate product of all elements and return it
val = np.prod(productElements)
return val
def getbasisfuncarray(x, expList):
""" builds an array (1-D) of basis functions for a polynom"""
basisfunclist = [getbasisfunc(x, expList[i]) for i in range(0, len(expList))]
return np.array(basisfunclist)
def main():
# dim d, polynomial degree k
d = 2
k = 5
# create n random sample points xSamples in [-5, 5]
n = 100
xSamples = np.divide(np.random.randint(-500, 500 + 1, (d,n)), 100)
# values of samples
valSamps = myfunc(xSamples)
# find exponents for polynomial model function
expList = getpolyexponents(d, k)
# define model
def polymodel(x, *args):
coeffArray = np.array([arg for arg in args])
basisFuncs = getbasisfuncarray(x, expList)
# scalar product / dot product
poly = np.dot(coeffArray, basisFuncs)
return poly
################################################
# fit data to model
#popt, pcov = curve_fit(polymodel, xSamples, valSamps, maxfev = 1000) #fails
popt, pcov = curve_fit(myfunc, xSamples, valSamps, maxfev = 1000) #works
################################################
# print fitting results
print("fitting results:")
print(popt)
print(pcov)
if __name__ == "__main__": main()-
myfunc: just the ackley function
-
getpolyexponents: Since I want to create a polynomial model, I need the exponents for all my variables (x1 and x2 or also called x and y in 2-D). For example: In 2D the polynomial basis functions are:
-
x^0*y^0
-
x^0*y^1
-
x^0*y^2
-
x^1*y^0
-
x^1*y^1
-
x^2*y^0
-
I get those exponents from this function. In the 2-D case there would be those 6 sets of exponents in the returned expList
-
-
getbasisfunc: Uses one exponent set from the expList from above function to create the actual basis function
-
getbasisfuncarray: puts all required basis functions in a 1-D Array.
I did not include the plotting part because it's in another module and would just be more code in here.
If there is a better way to fit my data to a polynomial model of d dimensions (d being up to 23 in my case) and degree k (k up to 10 approx.) with python I am happy to learn new stuff!
Thank you already in advance. Any tips are greatly appreciated!
Cheers
Check the doc of the function: https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.curve_fit.html
You can seen that if you don't pass p0 then the curve_fit tries to read the number of the arguments of your provided model function. Since you use *args it can't do that:
p0array_like, optional
Initial guess for the parameters (length N). If None, then the initial values will all be 1 (if the number of parameters for the function can be determined using introspection, otherwise a ValueError is raised).
How to fix this? Provide the inital values for your *args as array-like. maybe like this?:
popt, pcov = curve_fit(polymodel, xSamples, valSamps, p0= [1,1,1,1,1,1], maxfev = 1000)
Disclaimer: I never worked with scipy and I don't really get what you're doing here, but this is something I understood from the documentation. if somethings wrong please feel free to correct me!
You would give curve_fit() a list of initial coefficients via the p0 parameter.
popt, pcov = curve_fit(polymodel, xSamples, valSamps, p0=initial_coeffList, maxfev = 1000)
But when i try with simplest form (len(expList) is 21 for d=2, k=5)
initial_coeffList = [1] * len(expList)
popt, pcov = curve_fit(polymodel, xSamples, valSamps, p0=initial_coeffList, maxfev = 1000)
and rest of your code, curve_fit() ends with results but also with an OptimizeWarning