我正在两个不同的实验区域内拟合一组实验数据(sample),并可以用以下两个数学函数表示:
第一个区域:
y = m*x + c ( the slope can be constrained to zero)
第二个区域:
y = d*exp(-k*x)
下面显示了实验数据,我在python中将其编码如下:
def func(x, m, c, d, k):
return m*x+ c + d*np.exp(-k*x)
popt, pcov = curve_fit(func, t, y)
不幸的是,我的数据无法正确拟合,并且拟合(返回)的参数没有意义(请参见下图)。
任何帮助将不胜感激。
答案 0 :(得分:2)
由于您的数据在不同区域中表现出不同的行为,因此您还需要使数据适合这些不同区域。那不是对两个模型(函数)求和,而是应该用y = m*x + c
使左边的区域适合一个,而y = d*exp(-k*x)
则使右边的区域适合。如果您找不到两个区域的边界,可以通过比较goodness of fit来评估。
popt_1, pcov_1 = curve_fit(lambda x, m, c: m*x + c, t[t < 0.8], y[t < 0.8], p0=(1, 0))
popt_2, pcov_2 = curve_fit(lambda x, d, k: d*exp(-k*x), t[t >= 0.8], y[t >= 0.8], p0=(400, 1))
示例代码:
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from scipy.optimize import curve_fit
df = pd.read_csv('test.csv', index_col=None)
t = df.t.values
y = df.Y.values
boundary = t[y.argmax()]
t1 = t[t < boundary]
y1 = y[t < boundary]
t2 = t[t >= boundary]
y2 = y[t >= boundary]
f1 = lambda x, m, c: m*x + c
f2 = lambda x, d, k: d*np.exp(-k*x)
popt_1 ,pcov_1 = curve_fit(f1, t1, y1, p0=((y1[-1] - y1[0]) / (t1[-1] - t1[0]), y1[0]))
popt_2 ,pcov_2 = curve_fit(f2, t2, y2, p0=(y2[0], 1))
plt.title('Fitted data on two different domains')
plt.xlabel('t [a.u.]')
plt.ylabel('y [a.u.]')
plt.plot(t, y, '-o', label='Data')
plt.plot(t1, f1(t1, *popt_1), '--', color='#ff7f0e', lw=3, label='Fit')
plt.plot(t2, f2(t2, *popt_2), '--', color='#ff7f0e', lw=3, label='_nolegend_')
plt.grid()
plt.legend()
plt.show()
会产生以下情节:
请注意,结果“复合”函数在边界处不连续。如果不希望这样,您可以通过在拟合另一个域(一种方式或另一种)之前固定一个拟合参数(例如k
)来解决。或者,您可以分别拟合两个区域,然后将边界处的值确定为两个单独函数(即y_b = (f1(t1[-1], *popt_1) + f2(t2[0], *popt_2)) / 2
)的平均值,然后通过约束参数来满足该边界条件,从而重复拟合。>
例如,首先拟合线性函数,然后固定指数的d
参数,以便在边界处进行连续过渡(请注意,线性函数f1
是外推的< / em>在其域t2[0]
之外,以确保连续性):
f1 = lambda x, m, c: m*x + c
popt_1, pcov_1 = curve_fit(f1, t1, y1, p0=((y1[-1] - y1[0]) / (t1[-1] - t1[0]), y1[0]))
d = f1(t2[0], *popt_1)
f2 = lambda x, k: d*np.exp(-k*(x - boundary))
popt_2, pcov_2 = curve_fit(f2, t2, y2, p0=(1,))
会产生以下情节:
答案 1 :(得分:2)
非常有趣的问题。正如a_guest所说,您将不得不分别适合两个区域。但是,我想您可能还希望两个区域在t0
点(我们从一种模型切换到另一种模型的点)处平滑连接。为此,我们需要在点y1 == y2
处添加约束t0
。
为了使用scipy
执行此操作,请使用SLSQP方法查看scipy.optimize.minimize
。但是,我写了一个scipy
包装器来简化这种事情,称为symfit
。我将向您展示如何使用symfit
执行此操作,因为我认为它更适合该任务,但是在此示例中,如果您愿意,您也应该可以使用纯scipy
来实现它。>
from symfit import parameters, variables, Fit, Piecewise, exp, Eq
import numpy as np
import matplotlib.pyplot as plt
t, y = variables('t, y')
m, c, d, k, t0 = parameters('m, c, d, k, t0')
# Help the fit by bounding the switchpoint between the models
t0.min = 0.6
t0.max = 0.9
# Make a piecewise model
y1 = m * t + c
y2 = d * exp(- k * t)
model = {y: Piecewise((y1, t <= t0), (y2, t > t0))}
# As a constraint, we demand equality between the two models at the point t0
# to do this, we substitute t -> t0 and demand equality using `Eq`
constraints = [Eq(y1.subs({t: t0}), y2.subs({t: t0}))]
# Read the data
tdata, ydata = np.genfromtxt('Experimental Data.csv', delimiter=',', skip_header=1).T
fit = Fit(model, t=tdata, y=ydata, constraints=constraints)
fit_result = fit.execute()
print(fit_result)
plt.scatter(tdata, ydata)
plt.plot(tdata, fit.model(t=tdata, **fit_result.params).y)
plt.show()
答案 2 :(得分:1)