Я работаю над классом для работы с наборами данных "x, y". Данные обычно поступают из txt-файлов, где первый столбец данных хранится в «x», а второй столбец — в «y».
Я добавляю в класс некоторые функции "подгонки кривой". И я получаю ошибку в заголовке этого поста.
Вот класс:
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
Я вставляю класс в python в интерактивном режиме. А затем попробуйте следующее:
$ 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.
>>>
Я вставляю класс, показанный выше, а затем пробую следующее:
>>> 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>>
Я пытался убрать self из f00() и cf00(). Не работает.
Я попытался вызвать f00() из своего экземпляра, и это работает:
>>> xy=XY()
>>> xy.f00(1,1,1,1,1)
1.3678794411714423
Я использовал эту функцию подгонки кривой в другом месте, и она работает. Теперь я пытаюсь реализовать это в классе.
Вот автономные функции, которые работают, когда они не являются частью класса:
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)
Нет проблем.
Это содержит массу ненужного кода, который можно было бы обрезать, чтобы сделать этот MCVE
Кроме того, __init__ никогда не должен содержать оператор return.
Я не знаю, имеет ли это значение, но обычно мы не делаем импорт как часть определения класса. Обычно это делается в начале модуля. Иногда импорт выполняется внутри функции (или метода).
Нет. Я взял "я", чтобы посмотреть, устранит ли это проблему. Это не так.
Я рекомендую прочитать о классы






Итак, вы импортируете from scipy.optimize import curve_fit внутри определения класса, а python привязывает это имя к пространству имен класса, поэтому, когда вы вызываете self.curve_fit, это работает, но имейте в виду, что функция curve_fit определена внутри scipy.
Когда вы звоните self.method(), на самом деле происходит следующее:
type(self).method(self), поэтому, когда вы вызываете self.curve_fit, он передает класс XY в качестве первого параметра и ожидает функцию.
Чтобы решить эту проблему, я рекомендую поместить импорт в самый верх файла и просто вызвать curve_fit без self
Это работает. Я также узнал, что мне нужно использовать массивы numpy вместо списков для подбора кривой.
Хорошая заметка о self.curve_fit. То, что должен вызывается, это XY.curve_fit, поскольку оно импортируется на уровне класса, а не на уровне экземпляра.
Это большой блок кода, но я указал, что все должно быть исправлено в вашем коде, включая комментарии:
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
Для дальнейшего использования лучше включить только то, что необходимо для воспроизведения проблемы, с которой вы столкнулись, и ничего более. Это значительно упрощает локализацию проблемы. Минимальный рабочий пример может выглядеть следующим образом:
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)
Гораздо проще определить, что идет не так
Итак, проблема в том, когда вы берете
selfвне из определенияcf00?