Слишком много аргументов, используемых python scipy.optimize.curve_fit

#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 он не используется в функции.