我正在尝试拟合通常根据以下内容建模的数据:
def fit_eq(x, a, b, c, d, e):
return a*(1-np.exp(-x/b))*(c*np.exp(-x/d)) + e
x = np.arange(0, 100, 0.001)
y = fit_eq(x, 1, 1, -1, 10, 0)
plt.plot(x, y, 'b')
但是,实际跟踪的一个例子更嘈杂:
如果我分别适合上升和衰减的组件,我可以得到一些好的适合:
def fit_decay(df, peak_ix):
fit_sub = df.loc[peak_ix:]
guess = np.array([-1, 1e-3, 0])
x_zeroed = fit_sub.time - fit_sub.time.values[0]
def exp_decay(x, a, b, c):
return a*np.exp(-x/b) + c
popt, pcov = curve_fit(exp_decay, x_zeroed, fit_sub.primary, guess)
fit = exp_decay(x_full_zeroed, *popt)
return x_zeroed, fit_sub.primary, fit
def fit_rise(df, peak_ix):
fit_sub = df.loc[:peak_ix]
guess = np.array([1, 1, 0])
def exp_rise(x, a, b, c):
return a*(1-np.exp(-x/b)) + c
popt, pcov = curve_fit(exp_rise, fit_sub.time,
fit_sub.primary, guess, maxfev=1000)
x = df.time[:peak_ix+1]
y = df.primary[:peak_ix+1]
fit = exp_rise(x.values, *popt)
return x, y, fit
ix = df.primary.idxmin()
rise_x, rise_y, rise_fit = fit_rise(df, ix)
decay_x, decay_y, decay_fit = fit_decay(df, ix)
f, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4))
ax1.plot(rise_x, rise_y)
ax1.plot(rise_x, rise_fit)
ax2.plot(decay_x, decay_y)
ax2.plot(decay_x, decay_fit)
但理想情况下,我应该能够使用上面的等式拟合整个瞬态 . 不幸的是,这不起作用:
def fit_eq(x, a, b, c, d, e):
return a*(1-np.exp(-x/b))*(c*np.exp(-x/d)) + e
guess = [1, 1, -1, 1, 0]
x = df.time
y = df.primary
popt, pcov = curve_fit(fit_eq, x, y, guess)
fit = fit_eq(x, *popt)
plt.plot(x, y)
plt.plot(x, fit)
我已经为 guess
尝试了许多不同的组合,包括我认为应该是合理近似值的数字,但是我得到了非常合适或者curve_fit无法找到参数 .
我也尝试拟合较小的数据部分(例如0.12到0.16秒),但没有取得更大的成功 .
此特定示例的数据集副本在此处通过Share CSV
我在这里缺少任何提示或技巧吗?
编辑1:
因此,正如所建议的那样,如果我约束适合的区域不包括左侧的高原(即下图中的橙色),我会得到一个合适的选择 . 我遇到了另一篇关于curve_fit的stackoverflow帖子,其中提到转换非常小的值也有帮助 . 将时间变量从几秒转换为毫秒,在获得合适的体验方面有很大的不同 .
我还发现强迫curve_fit尝试通过几个点(特别是峰值,然后在衰变的拐点处的一些较大的点,因为那里的各种瞬态拉动衰减适应)有帮助 .
我想对于左边的高原,我可以拟合一条线并将其连接到指数拟合?我最终想要达到的目的是减去大的瞬态,所以我需要左侧的高原表示 .
sub = df[(df.time>0.1275) & (d.timfe < 0.6)]
def fit_eq(x, a, b, c, d, e):
return a*(1-np.exp(-x/b))*(np.exp(-x/c) + np.exp(-x/d)) + e
x = sub.time
x = sub.time - sub.time.iloc[0]
x *= 1e3
y = sub.primary
guess = [-1, 1, 1, 1, -60]
ixs = y.reset_index(drop=True)[100:300].sort_values(ascending=False).index.values[:10]
ixmin = y.reset_index(drop=True).idxmin()
sigma = np.ones(len(x))
sigma[ixs] = 0.1
sigma[ixmin] = 0.1
popt, pcov = curve_fit(fit_eq, x, y, p0=guess, sigma=sigma, maxfev=2000)
fit = fit_eq(x, *popt)
x = x*1e-3
f, (ax1, ax2) = plt.subplots(1,2, figsize=(16,8))
ax1.plot((df.time-sub.time.iloc[0]), df.primary)
ax1.plot(x, y)
ax1.plot(x.iloc[ixs], y.iloc[ixs], 'o')
ax1.plot(x, fit, lw=4)
ax2.plot((df.time-sub.time.iloc[0]), df.primary)
ax2.plot(x, y)
ax2.plot(x.iloc[ixs], y.iloc[ixs], 'o')
ax2.plot(x, fit)
ax1.set_xlim(-.02, .06)
2 回答
我尝试使用遗传算法将您的链接数据拟合到您发布的方程中,以进行初始参数估计,结果与您的结果类似 .
如果您可能使用另一个等式,我发现Weibull峰值方程(带有偏移量)给出了一个看起来很好的拟合,如附图所示
y = a * exp(-0.5 *(ln(x / b)/ c)2)偏移
我来自EE背景,寻找“系统识别”工具,但没有找到我在我发现的Python库中的预期
所以我在频域中制定了一个“天真”的SysID解决方案,我对此更为熟悉
我删除了初始偏移,假设步进激励,加倍,将数据集反转为fft处理步骤的周期性
用
scipy.optimize.least_squares
拟合拉普拉斯/频域传递函数后:我在同情的帮助下转换回时域步骤响应
inverse_laplace_transform(s*a/((s*b + 1)*(s*c + 1)*s), s, t
经过一点简化:
对频域拟合常数应用归一化,排列图
绿色:加倍,反转数据为周期性
红色:估计起始步骤,也加倍,倒转为周期性方波
黑色:频域拟合模型与方波卷积
黄色:拟合频域模型转换回时域步骤响应,滚动比较