scipy curve_fit无法适应tophat功能

时间:2018-04-17 12:52:50

标签: python scipy curve-fitting

我正在尝试为某些数据设置一个大礼帽功能,即。 f(x)对于整个实线是恒定的,除了一个有限长度的段等于另一个常数。我的参数是tophat函数的两个常量,中点和宽度,我正在尝试使用scipy.optimize.curve_fit来获取所有这些。不幸的是,curve_fit无法获得帽子的宽度。无论我做什么,它都拒绝测试除我开始之外的任何宽度值,并且非常严重地适应其余数据。以下代码段说明了问题:

import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit

def tophat(x, base_level, hat_level, hat_mid, hat_width):
    ret=[]
    for xx in x:
        if hat_mid-hat_width/2. < xx < hat_mid+hat_width/2.:
            ret.append(hat_level)
        else:
            ret.append(base_level)
    return np.array(ret)

x = np.arange(-10., 10., 0.01)
y = tophat(x, 1.0, 5.0, 0.0, 1.0)+np.random.rand(len(x))*0.2-0.1

guesses = [ [1.0, 5.0, 0.0, 1.0],
            [1.0, 5.0, 0.0, 0.1],
            [1.0, 5.0, 0.0, 2.0] ]

plt.plot(x,y)

for guess in guesses:
    popt, pcov = curve_fit( tophat, x, y, p0=guess )
    print popt
    plt.plot( x, tophat(x, popt[0], popt[1], popt[2], popt[3]) )

plt.show()

为什么在实现这一目标时,curve_fit非常糟糕,我该如何解决?

3 个答案:

答案 0 :(得分:2)

首先,tophat的定义可以使用numpy.where而不是循环:

def tophat(x, base_level, hat_level, hat_mid, hat_width):
    return np.where((hat_mid-hat_width/2. < x) & (x < hat_mid+hat_width/2.), hat_level, base_level)

其次,棘手的不连续目标函数抵抗curve_fit调用的优化算法。 Nelder-Mead方法通常适用于粗略函数,但看起来curve_fit不能使用它。所以我设置了一个目标函数(只是偏差绝对值的总和)并最小化:

def objective(params, x, y):
    return np.sum(np.abs(tophat(x, *params) - y))

plt.plot(x,y)

for guess in guesses:
    res = minimize(objective, guess, args=(x, y), method='Nelder-Mead')
    print(res.x)
    plt.plot(x, tophat(x, *(res.x)))

结果更好,因为从宽度为2的太宽的帽子开始,它会缩小到正确的大小(参见三个猜测中的最后一个)。

[9.96041297e-01 5.00035502e+00 2.39462103e-04 9.99759984e-01]
[ 1.00115808e+00  4.94088711e+00 -2.21340843e-05  1.04924153e-01]
[9.95947108e-01 4.99871040e+00 1.26575116e-03 9.97908018e-01]

不幸的是,当开始猜测是一个太窄的帽子时,优化器仍然卡住了。

fit function

您可以尝试其他优化方法/目标函数组合,但我还没有找到一个可以让帽子可靠扩展的方法。

要尝试的一件事是不要使用接近真实水平的参数;这有时可能会受到伤害与

guesses = [ [1.0, 1.0, 0.0, 1.0],
            [1.0, 1.0, 0.0, 0.1],
            [1.0, 1.0, 0.0, 2.0] ]

我曾经设法得到

[ 1.00131181  4.99156649 -0.01109271  0.96822019]
[ 1.00137925  4.97879423 -0.05091561  1.096166  ]
[ 1.00130568  4.98679988 -0.01133717  0.99339777]

这对所有三个宽度都是正确的。但是,这只是在几次尝试中的一些尝试(在优化过程的初始化中存在一些随机性)。其他一些具有相同初始点的尝试失败了;这个过程不够健全。

答案 1 :(得分:1)

就其性质而言,与curve_fit()一样的非线性最小二乘拟合与实数浮点数一起工作,并且不善于处理离散变量。在拟合过程中,对每个变量进行小的变化(例如,在1e-7级别),并且使用该小变化对拟合结果的影响来决定如何改变该变量以改善拟合。对于离散采样数据,hat_mid和/或hat_width的微小变化可能很容易小于数据点的间距,因此对拟合没有任何影响。这就是为什么curve_fit非常可怕&#34;在这个问题上。

您可能会发现给步骤提供有限的宽度(即与离散数据的步长相当)有助于更好地找到帽子边缘的位置。

答案 2 :(得分:0)

您也可以尝试适合 f = A0(-Erf [A4 *(u-A1)-A3] + Erf [A4 *(u + A1)-A3])+ A2

此处A0将与台阶高度成比例,A1将与台阶高度成比例,A2将是基线的垂直偏移,A3应该是台阶中央从0开始的水平距离和台阶的斜率将与A4成正比。