在两个不同区域内拟合实验数据

时间:2018-08-26 23:02:28

标签: python scipy curve-fitting

我正在两个不同的实验区域内拟合一组实验数据(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)

不幸的是,我的数据无法正确拟合,并且拟合(返回)的参数没有意义(请参见下图)。

任何帮助将不胜感激。

Here is the link to plotted data

3 个答案:

答案 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()

会产生以下情节:

Data plot

请注意,结果“复合”函数在边界处不连续。如果不希望这样,您可以通过在拟合另一个域(一种方式或另一种)之前固定一个拟合参数(例如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,))

会产生以下情节:

Continuous fit

答案 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()

enter image description here

答案 2 :(得分:1)

如果您希望使用一个方程式,我发现Hocket-Sherby方程“ y = b-(ba)* exp(-c *(x ** d))”似乎很适合您数据,得出R平方为0.99,RMSE为11.2,参数a = 1.1262189756312683E + 01,b = 3.2040596733114870E + 02,c = 3.9385197507261771E-01,d = -4.7723382040098095E + 00

modelplot