2

I am new to python, so my knowledge is inadequate. I have a datafile named "tlove_cc_seq2_k2_NL3.dat". I want to fit a curve to the data. The code I am using is as follows::

...

 import numpy as np import matplotlib.pyplot as plt from scipy.optimize import curve_fit import math import pandas as pd import lmfit from lmfit import Model import matplotlib.pyplot as plt from scipy.optimize import curve_fit from array import * def test(x, a, b, c): return (a + b*math.log(x) + c*math.log(x)**2) func = np.vectorize(test) data_k2_2fl_NL3=np.loadtxt('tlove_cc_seq2_k2_NL3.dat') plt.plot(data_k2_2fl_NL3[:,8], data_k2_2fl_NL3[:,5], 'b-', label='data') popt, pcov = curve_fit(func, data_k2_2fl_NL3[:,8], data_k2_2fl_NL3[:,5]) popt plt.plot(data_k2_2fl_NL3[:,8], func(data_k2_2fl_NL3[:,8], *popt), 'r-', label='fit: a=%5.3f, b=%5.3f, c=%5.3f' % tuple(popt)) popt, pcov = curve_fit(func, data_k2_2fl_NL3[:,8], data_k2_2fl_NL3[:,5], bounds=(-20, [30., 30., 20.5])) popt plt.plot(data_k2_2fl_NL3[:,8], func(data_k2_2fl_NL3[:,8], *popt), 'g--', label='fit: a=%5.3f, b=%5.3f, c=%5.3f' % tuple(popt)) plt.xlabel('x') plt.ylabel('y') plt.legend() plt.show() 

...

The error I am getting is as follows::

... ValueError Traceback (most recent call last) in 13 #y = data[:, 1] 14 plt.plot(data_k2_2fl_NL3[:,8], data_k2_2fl_NL3[:,5], 'b-', label='data') ---> 15 popt, pcov = curve_fit(func, data_k2_2fl_NL3[:,8], data_k2_2fl_NL3[:,5]) 16 popt 17

 ~/anaconda3/lib/python3.7/site-packages/scipy/optimize/minpack.py in curve_fit(f, xdata, ydata, p0, sigma, absolute_sigma, check_finite, bounds, method, jac, **kwargs) 678 args, varargs, varkw, defaults = _getargspec(f) 679 if len(args) < 2: --> 680 raise ValueError("Unable to determine number of fit parameters.") 681 n = len(args) - 1 682 else: ValueError: Unable to determine number of fit parameters. 

How can I resolve this? Thank you.

2 Answers 2

2

I think the problem is that the curve_fit function cannot determine the number of parameters by introspection because the function you are asking it to fit (test) is wrapped in the np.vectorize function.

I tried a minimal example where I used the test function un-vectorized and it worked:

import numpy as np import matplotlib.pyplot as plt from scipy.optimize import curve_fit def test(x, a, b, c): return (a + b*np.log(x) + c*np.log(x)**2) func = np.vectorize(test) #Create some dummy data x_data = list(range(1, 11)) y_data = np.log(x_data) + np.log(x_data)**2 + np.random.random(10) plt.plot(x_data, y_data, 'b-', label='data') popt, pcov = curve_fit(test, x_data, y_data) popt 

If you need vectorize for performance reasons you can also pass a parameter p0 an array of initial parameters. e.g:

popt, pcov = curve_fit(func, x_data, y_data, p0=[1,1,1]) 
Sign up to request clarification or add additional context in comments.

5 Comments

The problem is that I need to fit a dataset from a datafile. When I was not vectorizing I was getting an error like "TypeError: only size-1 arrays can be converted to Python scalars". As this is generated when an array of numbers is given as input in place of a single number, so I did vectorization. The main issue is I need to fit data from a file.
Can you try using np.log (like in my answer) in place of math.log in your test function? The numpy method accepts arrays. Did passing an intialization vector p0 not work?
Thanks a lot, it worked. Main issue was np.log. It takes array while math.log does not.
Great. If the answer solves your problem you can mark it as 'accepted' and then others know it is solved.
I've now upvoted the answer from M Newville as it shows a better use of lmfit. I hadn't used that package before and so rather narrowly got the errors to go away. It sounds like his answer shows a better way to use the package.
1

It seems that most of the issues you had were with using numpy vs math. For completeness, and since you mentioned lmfit, to do this with lmfit, you could use

import numpy as np import matplotlib.pyplot as plt from lmfit import Model def test(x, a, b, c): return (a + b*np.log(x) + c*np.log(x)**2) # create model from your model function mymodel = Model(test) # create initial set of named parameters from argument of your function params = mymodel.make_params(a=0.5, b=1.1, c=0.5) # Create some dummy data x_data = np.linspace(1, 10, 10) y_data = np.log(x_data) + np.log(x_data)**2 + np.random.random(len(x_data)) # run fit, get result result = mymodel.fit(y_data, params, x=x_data) # print out full fit report: fit statistics, best-fit values, uncertainties print(result.fit_report()) # make a stacked plot of residual and data + fit result.plot() plt.show() 

Note that curve_fit() will happily accept uninitialized parameters, assigning the impossible-to-justify default value of 1 for all parameters. Lmfit does not allow this and forces you to explicitly set initial values. But it also better reports fit statistics, uncertainties, and allows for composition of more complpex models.

For your example, the fit report will read

[[Model]] Model(test) [[Fit Statistics]] # fitting method = leastsq # function evals = 8 # data points = 10 # variables = 3 chi-square = 0.91573485 reduced chi-square = 0.13081926 Akaike info crit = -17.9061352 Bayesian info crit = -16.9983799 [[Variables]] a: 0.69752193 +/- 0.34404583 (49.32%) (init = 0.5) b: 1.17700278 +/- 0.59765274 (50.78%) (init = 1.1) c: 0.85298657 +/- 0.23838141 (27.95%) (init = 0.5) [[Correlations]] (unreported correlations are < 0.100) C(b, c) = -0.961 C(a, b) = -0.782 C(a, c) = 0.607 

and a plot of

enter image description here

1 Comment

Thanks for more detailed explanation and sharing your experience of lmfit. I hadn't used that package before.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.