困难的python curve_fit需要可选参数的多维任务

时间:2018-06-17 03:51:45

标签: python curve-fitting

我有一组数据由多个对象的(二维)观察组成。观察结果可以通过一般函数加上每个对象唯一的偏移量来描述。我想使用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]

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

有人能指出我正确的方向吗?

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

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

2 个答案:

答案 0 :(得分:1)

这是一个示例代码,它使用' A'来匹配具有共享参数的两个不同方程。或者' B'解码。它看起来像你需要解码实验室类型一样工作,但我个人从来没有这样做过,虽然它似乎按照你的帖子运行" text-to-float"函数内部的转换对我来说似乎很笨拙。但它确实有效。

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)

答案 1 :(得分:0)

显示您正在尝试的内容的更完整示例会很有帮助,包括对scipy.optimize.curve_fit的调用。但是,如果我正确理解了这个问题,你希望你的模型函数有一个参数,被视为拟合中的一个变量。我认为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. 您可以指定多个“自变量”,它们不必是该函数的第一个参数。

  3. 也就是说,你可以写:

    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(我是其中一个开发人员)添加了许多其他便利,用于构建曲线拟合模型并将参数作为高级对象使用,但这可能足以让您入门。