python - 使用 curve_fit 拟合梯形

标签 python scipy curve-fitting

我正在尝试使用 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')

对于大多数情况,它工作得很好,如下所示:
Good fit

但是,我也遇到了一些无法拟合数据的情况,但看起来应该没问题:

Bad fit

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

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

拟合不起作用的示例数组:

数组([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

最佳答案

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

特别是对于您的问题,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)

希望有帮助。

关于python - 使用 curve_fit 拟合梯形,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50289220/

相关文章:

python - python读取的复杂Matlab struct mat文件

python - 具有线性约束的非凸优化

python - 函数引用在循环外丢失

贝塞尔曲线和法国曲线

python - 带追加的列表累积

python - 高效,快速的numpy直方图

python - 在 python 中模拟传值行为

gnuplot - `set xrange [x_min:x_max]` 是否限制用于函数拟合的范围?

python-3.x - 我应该如何使用单变量样条曲线来拟合特定形状的数据?

python - 为什么该代码片段无法在 Google Colab 上运行?