【问题标题】:Fitting a trapezoid with curve_fit用curve_fit拟合梯形
【发布时间】:2018-10-21 16:30:30
【问题描述】:

我正在尝试使用 scipy.optimize 中的 curve_fit 库将梯形拟合到一组时间序列。我用来生成梯形的函数如下:

def trapezoid(x, a, b, c, tau1, tau2):
    y = np.zeros(len(x))
    c = -np.abs(c)
    a = np.abs(a)
    y[:int(tau1)] =  a*x[:int(tau1)] + b
    y[int(tau1):int(tau2)] =  a*tau1 + b 
    y[int(tau2):] = c*(x[int(tau2):]-tau2) + (a*tau1 + b)
    return y

其中 a 和 c 是斜率,tau1 和 tau2 标志着平坦阶段的开始和结束。

为了适应,我只使用:

popt, pcov = curve_fit(trapezoid, xdata, ydata, method = 'lm')

在大多数情况下,它都可以正常工作,例如:

但是,我也遇到了一些无法拟合数据的情况,看起来它应该可以正常工作:

这些情况的问题在于它设置的 tau2(平坦阶段的结束)小于 tau1(它的开始)。

谁能提出解决这个问题的方法?是通过施加约束还是以其他方式?

不适合的示例数组:

array([1.2, 1.21, 1.2, 1.19, 1.21, 1.22, 2.47, 2.53, 2.49, 2.39, 2.28, 2.16、2.07、1.99、1.91、1.83、1.74、1.65、1.57、1.5、1.45、1.41、 1.38, 1.35, 1.33, 1.29, 1.24, 1.19, 1.14, 1.11, 1.07, 1.04, 1. , 0.95, 0.91, 0.87, 0.84, 0.8, 0.77, 0.74, 0.72, 0.7, 0.68, 0.66, 0.63, 0.61, 0.59, 0.57, 0.55, 0.52, 0.5, 0.48, 0.45, 0.43, 0.41, 0.39, 0.38, 0.37, 0.37, 0.36, 0.35, 0.34, 0.34, 0.33])

产生:tau1:8.45,tau2:5.99

【问题讨论】:

  • 将 tau2 设置为 tau1 和 tau2 的最大值,就完成了
  • 由于初始解决方案错误,优化器似乎陷入了局部最小值。您没有为 curve_fit 提供 p0 参数,因此您的梯形参数都初始化为 1。您可能想找到一种方法来正确初始化参数,否则最好使用全局优化器,例如 SciPy 的 differential_evolution。
  • @Glostas,问题是当它由于这个原因而无法适应时,它会分配一个完全错误的 tau1 和 tau2,强加你提到的分配并不能解决问题。理想的情况是能够添加一个强制 tau2>tau1 的约束,但显然使用这个库是不可能的。
  • 与其将 tau2 定义为平坦阶段的索引,不如将其定义为其长度,而 tau1 仍标记其开始.
  • @Mr.T 不,这不是假设,只是更新了一个例子

标签: python scipy curve-fitting


【解决方案1】:

只需将 t1,t2 设置为最小值和最大值即可

def trapezoid(x, a, b, c, tau1, tau2):
    y = np.zeros(len(x))
    c = -np.abs(c)
    a = np.abs(a)
    (tau1,tau2) = (min(tau1,tau2),max(tau1,tau2))

    y[:int(tau1)] =  a*x[:int(tau1)] + b
    y[int(tau1):int(tau2)] =  a*tau1 + b 
    y[int(tau2):] = c*(x[int(tau2):]-tau2) + (a*tau1 + b)



x_data = np.arange(len(A))
popt, pcov = curve_fit(trapezoid, x_data, A, method = 'lm')
print popt
fit = trapezoid(x_data,*popt)

导致:

【讨论】:

  • 我试过了,它仍然给出了一些错误的结果。试试这个数组([1.2, 1.21, 1.2, 1.19, 1.21, 1.22, 2.47, 2.53, 2.49, 2.39, 2.28, 2.16, 2.07, 1.99, 1.91, 1.83, 1.74, 1.65, 1.58, 1.5 , 1.3.4 , 1.35, 1.33, 1.29, 1.24, 1.19, 1.14, 1.11, 1.07, 1.04, 1. , 0.95, 0.91, 0.87, 0.84, 0.8, 0.77, 0.74, 0.72, 0.7, 0.5, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.57, 0.55, 0.52, 0.5, 0.48, 0.45, 0.43, 0.41, 0.39, 0.38, 0.37, 0.37, 0.36, 0.35, 0.34, 0.34, 0.33])
  • 我刚才展示的示例,我的解决方案给出了 tau1=7.77 和 tau2=7.85,这给了你一个梯形。它看起来像一个三角形
【解决方案2】:

您可能会发现 lmfit (http://lmfit.github.io/lmfit-py/) 对这个问题很有用。 Lmfit 为曲线拟合提供了更高级别的接口,仍然基于scipyoptimizers,但具有一些更好的抽象和功能。

特别是对于您的问题,lmfit 参数是 Python 对象,它们可以具有边界、固定或根据其他变量写成简单的代数约束。这可以支持强加tau2 > tau1。 这个想法本质上是设置tau2=tau1+taudiff 并将taudiff 的下限设置为0。虽然您可以在代码中重写您的函数来执行此操作,但使用 lmfit 您不必这样做,而是可以将该逻辑放在参数中。

将您的脚本转换为使用 lmfit 会得到如下结果:

from lmfit import Model

# use your same model function
def trapezoid(x, a, b, c, tau1, tau2):
    y = np.zeros(len(x))
    c = -np.abs(c)
    a = np.abs(a)
    y[:int(tau1)] =  a*x[:int(tau1)] + b
    y[int(tau1):int(tau2)] =  a*tau1 + b 
    y[int(tau2):] = c*(x[int(tau2):]-tau2) + (a*tau1 + b)
    return y

# turn model function into lmfit Model
tmod = Model(trapezoid)

# create Parameters for this model: they will be *named* according
# to the signature of the model function, and be used as keys in
# an ordered-directory-derived object.  Here you can also give
# initial values
params = tmod.make_params(a=1, b=2, c=0.5, tau1=5, tau2=-1)

# now you can set bounds or constraints.  

# 1st, add a new variable "taudiff"
params.add('taudiff', value=0.1, min=0, vary=True)

# constraint tau2 to be taudiff+tau1 -- this is no longer a "free variable:
params['tau2'].expr = "taudiff + tau1"

# now do fit to data:
result = tmod.fit(ydata, params, x=xdata)

# print report of fit
print(result.fit_report())

# get best fit params:
for parname, param in result.params:
    print(parname, param.value, param.stderr, param.expr)

# get best fit array for plotting
pylab.plot(xdata, ydata)
pylab.plot(xdata, result.best_fit)

希望对您有所帮助。

【讨论】:

  • 非常感谢,这正是我想要的,效果很好。
猜你喜欢
  • 2019-02-12
  • 2012-06-07
  • 2018-03-29
  • 1970-01-01
  • 2020-05-18
  • 2019-01-23
  • 1970-01-01
  • 2016-11-29
  • 2012-11-02
相关资源
最近更新 更多