【问题标题】:difficult python curve_fit multi-dimensional task requiring an optional argument需要可选参数的困难 python curve_fit 多维任务
【发布时间】:2018-11-26 08:34:59
【问题描述】:

我有一组由多个对象的(二维)观察组成的数据。观察可以用一个通用函数加上每个对象唯一的偏移量来描述。我想使用curve_fit同时恢复每个对象的通用函数和偏移量(带有相关错误)。我事先不知道数据集将由多少个对象组成,只知道每个对象可能有多个观察值。

因此,包含 7 个观察值的广义数据集可能如下所示:

[[x[0], y1[0], y2[0], lab='A'], 
[x[1], y1[1], y2[1], lab='B'],
[x[2], y1[2], y2[2], lab='A'],
[x[3], y1[3], y2[3], lab='A'],
[x[4], y1[4], y2[4], lab='B'],
[x[5], y1[5], y2[5], lab='C'],
[x[6], y1[6], y2[6], lab='A']]

我可以通过将通用函数的参数(例如 g = [g0, g1, g2])和对象偏移量 offsets = nx [o1, o2] 传递给 fit_func 然后使用对象标签来决定来完成任务这n个偏移量中的哪一个需要加到通用函数中,除了我不知道如何传递标签。

def fit_func(x, g, offsets, lab):
    y1 = g[0] * cos(2*(x - g[1])) + offsets['lab',0] + g[2]
    y2 = g[0] * sin(2*(x - g[1])) + offsets['lab',1] + g[2]
    return [y1, y2]

问题是 lab 不是一个适合的浮点数,所以我不知道如何通过它。通过阅读其他一些线程,我相信我需要一个包装函数,但我无法弄清楚它应该采用什么形式,以及如何以我可以指定 sigma 和 p0 的方式调用它。

谁能指出我正确的方向?

编辑:我设法生成了一个我认为可行的函数。它使用全局参数调用来选择函数调用中的选项。因此,例如,我交错了 y1 和 y2 数组,并让函数在每秒运行一次时调用第二个方程,并使用全局 getEven() 和 setEven(bool) 调用。然而 curve_fit 真的不喜欢那样。拟合值是荒谬的。

目前我正在分别拟合 y1 的方程和 y2 的方程并取 rms 来确定 g0 和 g1(这也给了我 offsets['A',0] 和 offsets['A',1]分别。我可以对集合中的每个不同对象多次执行此操作,但我不能以这种方式拟合 g2 参数,因为在对 y1 或 y2 函数的任何给定调用中,它都会退化并具有相应的偏移量。

【问题讨论】:

    标签: python curve-fitting


    【解决方案1】:

    以下示例代码使用“A”或“B”解码来拟合具有共享参数的两个不同方程。它似乎可以按照您解码实验室类型的需要工作,但我个人以前从未这样做过,虽然它似乎在您的帖子中起作用,但函数内部的“文本到浮点”转换对我来说似乎很笨拙。但它有效。

    import numpy
    import matplotlib
    import matplotlib.pyplot as plt
    from scipy.optimize import curve_fit
    
    # single array with all "X" data to pass around
    num = numpy.array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0])
    ids = numpy.array(['A', 'A', 'A', 'A', 'B', 'B', 'B', 'B'])
    xdata = numpy.array([num, ids]) # combine data, numpy auto-converts to 'text' type
    
    # ydata is numeric single array
    ydata = [9.0,8.0,7.0,6.0,4.0,3.0,2.0,1.0]
    
    
    def fitFunction(data, commonParameter, pA, pB):
        numericDataAsText = data[0]
        textData = data[1]
        returnArray = []    
        for i in range(len(textData)):
            x = float(numericDataAsText[i])
            if textData[i] == 'A':
                val = commonParameter + x * pA
            elif textData[i] == 'B':
                val = commonParameter + x * pB
            else:
                raise(Exception('Error: must use A or B'))
            returnArray.append(val)
        return returnArray
    
    
    initialParameters = [1.0, 1.0, 1.0]
    
    # curve fit the equations individually to their respective data
    params, pcov = curve_fit(fitFunction, xdata, ydata, initialParameters)
    
    # values for display of fitted function
    commonParameter, pA, pB = params
    
    # for plotting the fitting results
    y_fit = fitFunction(xdata, commonParameter, pA, pB)
    
    plt.plot(xdata[0], ydata, 'D') # plot the raw data as a scatterplot
    plt.plot(xdata[0][:4], y_fit[:4])
    plt.plot(xdata[0][4:], y_fit[4:])
    plt.show()
    
    print('fittedparameters:', params)
    

    【讨论】:

    • 感谢您充实我的建议!我认为这有点像 hack,但如果有人坚持使用 curve_fit,这是唯一的解决方法。
    • 我会仔细考虑您的评论。
    【解决方案2】:

    显示一个更完整的示例来说明您正在尝试的内容会很有帮助,包括对scipy.optimize.curve_fit 的调用。但是,如果我正确理解了这个问题,您希望您的模型函数有一个参数,该参数 not 被视为适合的变量。我相信curve_fit 无法做到这一点,并将第一个参数之后的所有参数视为变量。

    事实上,我认为您的模型函数不适用于curve_fit,因为您希望g 是一个值序列。使用curve_fit,第一个参数之后的每个参数都将获得一个浮点值。所以你可能想要类似的东西

    def func(x, g0, g1, g2, offsets):
        y1 = g0 * cos(2*(x - g1)) + offsets['lab', 0] + g2
        ...
    

    无论如何,我有两个建议可以解决curve_fit 的限制:

    首先,您可以重载x。现在,curve_fit 将在内部将numpy.asarray() 应用于您传入的x,但否则它只会将其传递给您的模型函数。所以,如果你把x 变成一个包含你真正的xlab 的列表,你应该可以在你的模型函数中解压它,比如

    xhack = [x, offsets]
    def func(x, g0, g1, g2):
        x, offsets = x
        ....
    
    out = curve_fit(func, xhack, ...)
    

    就个人而言,我认为这有点难看,但它可能会奏效。

    其次,你可以使用 lmfit (https://lmfit.github.io/lmfit-py/),它为曲线拟合提供了更高级别的接口,并修复了curve_fit 的许多缺点。特别是对于您的问题,lmfit 的 Model 曲线拟合类会更仔细地检查模型函数,以将函数参数转换为拟合参数。具体来说:

    1. 具有非数字默认值的关键字参数不会被转换为适合参数。

    2. 您可以指定多个“自变量”,它们不必是函数的第一个参数。

    也就是说,你可以写:

    from lmfit import Model
    def func(x, g0, g1, g2, offsets=None):
        y1 = g0 * cos(2*(x - g1)) + offsets['lab', 0] + g2
    
    mymodel = Model(func)
    

    或明确告诉Model自变量是什么:

    from lmfit import Model
    def func(x, g0, g1, g2, offsets):
        y1 = g0 * cos(2*(x - g1)) + offsets['lab', 0] + g2
    
    mymodel = Model(func, independent_vars=['x', 'offsets'])
    

    无论哪种方式,offsets 可以是任何复杂对象,您可以使用此mymodel 进行曲线拟合:

    # create parameter objects for this model, with initial values:
    params = mymodel.make_params(g0=0, g1=0.5, g2=2.0)
    
    # run the fit
    result = mymodel.fit(ydata, params, x=x, offsets=offsets)
    

    我们为 lmfit(我是开发人员之一)添加了许多其他便利,用于构建曲线拟合模型并将参数用作高级对象,但这可能足以让您入门。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-24
      • 2017-10-01
      相关资源
      最近更新 更多