Слишком много аргументов, используемых python scipy.optimize.curve_fit - PullRequest
2 голосов
/ 01 октября 2011

Я пытаюсь выполнить некоторую подгонку кривой в методе экземпляра класса, и функция 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, чтобы дать ему больше параметров, которые не соответствуют параметрам, используемым кривой_fit. Это возможно?

Ответы [ 5 ]

2 голосов
/ 05 октября 2011

Неудачный случай и, возможно, ошибка в 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)

edit: избегать скаляра в качестве начального значения

>>> np.isscalar([2])
False

означает, что пример только с одним параметром работает, если начальное значение определено как [...], например, как в примере ниже:

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 голос
/ 01 октября 2011

Если вы определите 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 ) 
0 голосов
/ 02 октября 2016

Единственный питонический способ справиться с этим - дать Python знать, get_flow - это staticmethod: функция, которую вы помещаете в класс, потому что концептуально она принадлежит ей, но не должна быть, и поэтому не не нужно 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 можно узнать по тому факту, что self не используется в функции.

0 голосов
/ 02 октября 2011

Первый аргумент определения метода класса всегда должен быть self.Он передается автоматически и ссылается на вызывающий класс, поэтому метод всегда получает на один аргумент больше, чем вы передаете при его вызове.

0 голосов
/ 01 октября 2011

Попытка отбросить «себя» и сделать вызов: spopt.curve_fit(get_flow, pressure_drop, flow)

...