#python #scipy #curve-fitting
#python #scipy #подгонка кривой
Вопрос:
Я пытаюсь выполнить некоторую подгонку кривой в методе экземпляра класса, а функция curve_fit предоставляет моему методу экземпляра класса слишком много аргументов.
Код
class HeatData(hx.HX):
"""Class for handling data from heat exchanger experiments."""
затем несколько строк методов, которые работают нормально, тогда моя функция:
def get_flow(pressure_drop, coeff):
"""Sets flow based on coefficient and pressure drop."""
flow = coeff * pressure_drop**0.5
return flow
и вызов функции curve_fit
def set_flow_array(self):
"""Sets experimental flow rate through heat exchanger"""
flow = self.flow_data.flow
pressure_drop = self.flow_data.pressure_drop
popt, pcov = spopt.curve_fit(self.get_flow, pressure_drop, flow)
self.exh.flow_coeff = popt
self.exh.flow_array = ( self.exh.flow_coeff * self.exh.pressure_drop**0.5 )
выдает ошибку
get_flow() takes exactly 2 arguments (3 given)
Я могу заставить его работать, определив get_flow вне класса и вызвав его следующим образом:
spopt.curve_fit(get_flow, pressure_drop, flow)
но это нехорошо, потому что это действительно должен быть метод внутри класса, чтобы быть настолько универсальным, насколько я хочу. Как я могу заставить эту работу работать как метод экземпляра класса?
Я также хотел бы иметь возможность передавать self в get_flow, чтобы предоставить ему больше параметров, которые не соответствуют параметрам, используемым curve_fit . Возможно ли это?
Комментарии:
1. Я думаю, мне просто нужен способ вызвать get_flow без вызова ссылки на self, что, по-видимому, выполняется автоматически для связанных методов, независимо от того, передается ли «self» в качестве аргумента или нет.
Ответ №1:
Неудачный случай и, возможно, ошибка в curve_fit. curve_fit использует inspect для определения количества начальных значений, которые запутываются или вводятся в заблуждение, если есть лишнее self
.
Я подумал, что предоставление начального значения должно избежать проблемы. Однако в условии также есть значение isscalar(p0), я понятия не имею, почему, и я думаю, было бы неплохо сообщить об этом как о проблеме или ошибке:
if p0 is None or isscalar(p0):
# determine number of parameters by inspecting the function
import inspect
args, varargs, varkw, defaults = inspect.getargspec(f)
редактировать: избегая скаляра в качестве начального значения
>>> np.isscalar([2])
False
означает, что пример только с 1 параметром работает, если начальное значение определено как […], например, аналогично примеру ниже:
mc.optimize([2])
Пример с двумя аргументами и заданным начальным значением позволяет избежать вызова inspect, и все в порядке:
import numpy as np
from scipy.optimize import curve_fit
class MyClass(object):
def get_flow(self, pressure_drop, coeff, coeff2):
"""Sets flow based on coefficient and pressure drop."""
flow = coeff * pressure_drop**0.5 coeff2
return flow
def optimize(self, start_value=None):
coeff = 1
pressure_drop = np.arange(20.)
flow = coeff * pressure_drop**0.5 np.random.randn(20)
return curve_fit(self.get_flow, pressure_drop, flow, p0=start_value)
mc = MyClass()
print mc.optimize([2,1])
import inspect
args, varargs, varkw, defaults = inspect.getargspec(mc.get_flow)
print args, len(args)
РЕДАКТИРОВАТЬ: эта ошибка была исправлена, поэтому связанные методы теперь могут передаваться в качестве первого аргумента для curve_fit, если у вас достаточно новая версия scipy.
Фиксация отправки исправления ошибки на github
Комментарии:
1. Отлично! Сейчас я запускаю его следующим образом:
popt, pcov = spopt.curve_fit(self.get_flow, pressure_drop,flow, p0=np.array([self.exh.flow_coeff]))
и это работает прекрасно. Я согласен, что это ошибка, потому что, если я даю значение для p0 , оно должно использовать это для определения количества аргументов, даже если p0 является скаляром.
Ответ №2:
Если вы определяете get_flow
внутри своего HeatData
класса, вы должны иметь self
в качестве первого параметра : def get_flow(self, pressure_drop, coeff):
РЕДАКТИРОВАТЬ: после поиска определения curve_fit я обнаружил, что прототип таков curve_fit(f, xdata, ydata, p0=None, sigma=None, **kw)
, что первый аргумент должен быть вызываемым, который будет вызываться с первым аргументом в качестве независимой переменной: попробуйте с закрытием :
def set_flow_array(self):
"""Sets experimental flow rate through heat exchanger"""
flow = self.flow_data.flow
pressure_drop = self.flow_data.pressure_drop
def get_flow((pressure_drop, coeff):
"""Sets flow based on coefficient and pressure drop."""
#here you can use self.what_you_need
# you can even call a self.get_flow(pressure_drop, coeff) method :)
flow = coeff * pressure_drop**0.5
return flow
popt, pcov = spopt.curve_fit(get_flow, pressure_drop, flow)
self.exh.flow_coeff = popt
self.exh.flow_array = ( self.exh.flow_coeff * self.exh.pressure_drop**0.5 )
Комментарии:
1. Я попробовал это, и тогда ошибка
get_flow() takes exactly 3 arguments (4 given)
2. Попробуйте добавить self в качестве параметра при вызове curve_fit(self.get_flow, self, …)
3. В настоящее время у меня есть
def get_flow(self, pressure_drop, coeff):
иspopt.curve_fit(self.get_flow, pressure_drop, flow)
и это все еще выдает ошибкуget_flow() takes exactly 3 arguments (4 given)
4. Что произойдет, если вы вызовете
spopt.curve_fit(self.get_flow, self, pressure_drop, flow)
?5. Классная идея, но это то, что она делает:
get_flow() takes exactly 2 arguments (16 given)
Ответ №3:
Попытка отбросить «self» и выполнить вызов: spopt.curve_fit(get_flow, pressure_drop, flow)
Комментарии:
1. Если get_flow определен как метод HeatData, то я получаю следующую ошибку, отбрасывая self:
global name 'get_flow' is not defined
Ответ №4:
Первым аргументом определения метода класса всегда должен быть self . Это передается автоматически и ссылается на вызывающий класс, поэтому метод всегда получает на один аргумент больше, чем вы передаете при его вызове.
Комментарии:
1. Что довольно странно / круто, так это то, что слово «self» — это просто соглашение, и я мог бы использовать слово «hornswaggle» вместо self. Я думаю, это было бы не очень описательной практикой кодирования.
Ответ №5:
Единственный pythonic способ справиться с этим — сообщить Python, что get_flow
это функция a staticmethod
: a, которую вы помещаете в класс, потому что концептуально она принадлежит там, но это не обязательно и, следовательно, не нужно self
.
@staticmethod
def get_flow(pressure_drop, coeff):
"""Sets flow based on coefficient and pressure drop."""
flow = coeff * pressure_drop**0.5
return flow
staticmethod
‘s можно распознать по тому факту, что self
он не используется в функции.