Python scipy.optimize.curve_fit 使用了过多参数

2 投票
5 回答
2297 浏览
提问于 2025-04-17 03:27

我正在尝试在一个类的方法里进行曲线拟合,但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使用的拟合参数。这样做可以吗?

5 个回答

0

试着去掉“self”,然后这样调用:spopt.curve_fit(get_flow, pressure_drop, flow)

1

如果你在你的 HeatData 类里面定义 get_flow 方法,那么第一个参数必须是 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 ) 
2

这真是个倒霉的情况,可能还有一个在curve_fit里的bug。curve_fit会用inspect来判断起始值的数量,如果多了一个self,它就会搞混或者被误导。

我原以为给一个起始值就能避免这个问题。然而,条件里还有个iscale(p0),我不知道为什么会有这个,我觉得应该把这个当作问题或bug报告一下:

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

这意味着如果起始值定义为[...],比如下面的例子,只有一个参数的情况就能正常工作:

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)

补充:这个bug已经修复了,所以现在可以把绑定的方法作为curve_fit的第一个参数传入,只要你有一个足够新的scipy版本。
在github上提交的bug修复记录

撰写回答