【发布时间】:2017-07-13 10:06:08
【问题描述】:
我正在尝试拟合通常通过以下方式建模的数据:
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)
【问题讨论】:
-
我很确定您不能将该功能应用于任何具有两个“平端”的东西。您的拟合函数本质上是指数的差异,每个指数都有不同的一面。这些您可以一次性取消,仅此而已。如果你在左边扩展你的第一个情节,你会看到不是一个高原而是分歧。所以我的猜测是你必须截断或寻找另一个函数。
-
是的,它看起来像一个阶跃响应 - 所以你需要包含阶跃函数,可能类似于
np.signbit(t0-x)并适合它的转换时间,subs x = x * np.signbit(t0-x ) 在你的指数中 -
是否可以限制两个或三个参数(如果您知道峰值将始终具有相同的幅度或宽度)?或者,如果您通过替换
(x-x_0)以适应要由拟合确定的峰的起始位置来抵消拟合,会发生什么?
标签: python scipy curve-fitting