Reputation: 431
I am working on a class to work with "x,y" data sets. The data typically comes from txt files, where the first column of data is stored in "x" and the second columns is stored in "y".
I am adding some "curve fitting" functionality to the class. And I am getting the error in the title of this post.
Here is the class:
class XY(object):
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
import numpy as np
def __init__(self,f=None):
self.file(f)
self.read()
return
def file(self,f=None):
self.filename=self.get_filename(f)
return
def filename(self):
return self.filename
def get_filename(self,f):
if(type(f)==str):
filename=f
elif(type(f)==file):
filename=f.name
else:
filename=None
return filename
def read(self,f=None):
if(f is None):
if(self.filename is None):
return
else: # Use internal filename
filename=self.filename
else: # Change/set internal filename
self.filename=self.get_filename(f)
filename=self.filename
data=[]
try:
with open(filename,'r') as F:
for line in F:
data.append(line.split())
except IOError as e:
print("%s"%e)
return
F.close()
for r in range(0,len(data)):
for c in range(0,len(data[r])):
data[r][c]=float(data[r][c])
self.data=data
self.x=[self.data[i][0] for i in range(0,len(self.data))]
self.y=[self.data[i][1] for i in range(0,len(self.data))]
return self.data
def f00(self,x,a,b,c,d):
return a*x**b*self.np.exp(-c*x)+d
def cf00(self):
popt,pcov=self.curve_fit(self.f00,self.x,self.y)
self.y=self.f00(self.x,*popt)
return self.y
I paste the class into python in interactive mode. And then try the following:
$ python
Python 2.7.14 (default, Oct 31 2017, 21:12:13)
[GCC 6.4.0] on cygwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
I paste in the class shown above and then try the following:
>>> xy=XY("rot-03-05.dat")
>>> len(xy.x)
220
>>> len(xy.y)
220
>>> xy.cf00()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 56, in cf00
File "/usr/lib/python2.7/site-packages/scipy/optimize/minpack.py", line 751, in curve_fit
res = leastsq(func, p0, Dfun=jac, full_output=1, **kwargs)
File "/usr/lib/python2.7/site-packages/scipy/optimize/minpack.py", line 383, in leastsq
shape, dtype = _check_func('leastsq', 'func', func, x0, args, n)
File "/usr/lib/python2.7/site-packages/scipy/optimize/minpack.py", line 27, in _check_func
res = atleast_1d(thefunc(*((x0[:numinputs],) + args)))
File "/usr/lib/python2.7/site-packages/scipy/optimize/minpack.py", line 463, in func_wrapped
return func(xdata, *params) - ydata
TypeError: 'XY' object is not callable
>>> xy.cf00
<bound method XY.cf00 of <__main__.XY object at 0x6ff5ea25fd0>>
I tried taking self
out of f00()
and cf00()
. Didn't work.
I tried calling f00()
from my instance, and it works:
>>> xy=XY()
>>> xy.f00(1,1,1,1,1)
1.3678794411714423
I used this curve fitting function elsewhere, and it works. Now, I am trying to implement it in a class.
Here are standalone functions that work when they're not part of a class:
def f00(x,a,b,c,d): return a*x**b*np.exp(-c*x)+d
def cf00(x,y):
popt,pcov=curve_fit(f00,x,y,maxfev=1200000)
return f00(x,*popt)
y1=cf00(x,y)
No problems.
Upvotes: 0
Views: 680
Reputation: 823
So you are importing from scipy.optimize import curve_fit
inside the class definition and python is binding that name to the class namespace, therefore when you call self.curve_fit
it works, but keep in mind that curve_fit
function is defined inside scipy
.
When you call self.method()
what is really happening is:
type(self).method(self)
, so when you call self.curve_fit
it is passing the class XY
as the first parameter and it expects a function.
To solve that i recommend to put the imports at the very top of the file and just call curve_fit
without the self
Upvotes: 2
Reputation: 13106
This is a large code block, but I've outlined what all should be fixed in your code including comments:
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
import numpy as np
from io import TextIOWrapper # to check file-handle type
# Import modules at the global level, makes life easier
class XY(object):
def __init__(self,f=None):
self.filename = self.get_filename(f)
self.data, self.x, self.y = self.read()
# Don't return from __init__
# return
# This function is unnecessary
# def file(self,f=None):
# self.filename=self.get_filename(f)
# return
# This doesn't do anything, just access the attribute like instance.filename
# def filename(self):
# return self.filename
def get_filename(self,f):
if isinstance(f, str): # use isinstance for type checking
filename=f
elif isinstance(f, TextIOWrapper):
filename=f.name
else:
filename=None # I'd probably raise an exception here
return filename
def read(self): # the f param wasn't used otherwise
# The whole block here was just a repeat of self.get_filename, no
# need for it
data=[]
try:
with open(filename,'r') as F:
for line in F:
data.append(line.split())
except IOError as e:
# I would just let the exception happen and raise it
print("%s"%e)
raise
# return # Don't just return None, you'll get weird behavior
# F is already closed, that's what with does
# F.close()
for r in range(0,len(data)):
for c in range(0,len(data[r])):
data[r][c]=float(data[r][c])
x = [self.data[i][0] for i in range(0,len(self.data))]
y = [self.data[i][1] for i in range(0,len(self.data))]
return data, x, y # It's generally bad form to assign self.attr outside of __init__
def f00(self,x,a,b,c,d):
return a*x**b*np.exp(-c*x)+d
def cf00(self):
popt, pcov = curve_fit(self.f00, self.x, self.y) # you don't need self.curve fit since it's now globally imported
self.y=self.f00(self.x, *popt)
return self.y # Not sure you need to return this, since you can just access it after the function is called
For future reference, it's best to include only what is needed to replicate the problem you are facing, nothing more. It makes it much easier to isolate the problem. A minimal working example might look like the following:
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
import numpy as np
def XY(object):
def __init__(self):
self.x = [float(a) for a in range(100)]
self.y = [float(a) for a in range(100)]
self.data = list(zip(self.x, self.y))
def f00(self,x,a,b,c,d):
return a*x**b*np.exp(-c*x)+d
def cf00(self):
popt, pcov = curve_fit(self.f00, self.x, self.y)
return self.f00(self.x, *popt)
Much easier to identify what's going wrong
Upvotes: 0