如何使用 python 拟合多条指数曲线
How to fit multiple exponential curves using python
对于单个指数曲线,如图中所示 curve_fit for as single exponential curve , I am able to fit the data using scipy.optimize.curve_fit. However, I am unsure on how to realize a fit for similar dataset composed of multiple exponential curves as shown here double exponential curves。
我使用以下方法实现了对单曲线的拟合:
def exp_decay(x,a,r):
return a * ((1-r)**x)
x = np.linspace(0,50,50)
y = exp_decay(x, 400, 0.06)
y1 = exp_decay(x, 550, 0.06) # this is to be used to append to y to generate two curves
pars, cov = curve_fit(exp_decay, x, y, p0=[0,0])
plt.scatter(x,y)
plt.plot(x, exp_decay(x, *pars), 'r-') #this realizes the fit for a single curve
yx = np.append(y,y1) #this realizes two exponential curves (as shown above - double exponential curves) for which I don't need to fit a model to
有人可以帮助描述如何为两条曲线的数据集实现这一点。我的实际数据集包含多条指数曲线,但我认为如果我能实现两条曲线的拟合,我也许能够为我的数据集复制相同的曲线。 scipy 的 curve_fit 不能这样做;任何有效的实现都很好。
请帮忙!!!
您的问题可以通过使用简单的标准(例如一阶导数估计)拆分数据集轻松解决,然后我们可以对每个子数据集应用简单的曲线拟合程序。
试用数据集
首先,让我们导入一些包并创建一个包含三个曲线的合成数据集来表示您的问题。
我们使用两个参数的指数模型,因为时间原点偏移将由拆分方法处理。我们还添加了噪声,因为真实世界的数据总是存在噪声:
import numpy as np
import pandas as pd
from scipy import optimize
import matplotlib.pyplot as plt
def func(x, a, b):
return a*np.exp(b*x)
N = 1001
n1 = N//3
n2 = 2*n1
t = np.linspace(0, 10, N)
x0 = func(t[:n1], 1, -0.2)
x1 = func(t[n1:n2]-t[n1], 5, -0.4)
x2 = func(t[n2:]-t[n2], 2, -1.2)
x = np.hstack([x0, x1, x2])
xr = x + 0.025*np.random.randn(x.size)
图形呈现如下:
数据集拆分
我们可以使用一个简单的标准将数据集分成三个 sub-datasets 作为一阶导数估计,使用一阶差分来评估它。目标是检测曲线何时急剧上升或下降(数据集应在何处拆分。一阶导数估计如下):
dxrdt = np.abs(np.diff(xr)/np.diff(t))
标准需要一个额外的参数(阈值),必须根据您的信号规格进行调整。该标准相当于:
xcrit = 20
q = np.where(dxrdt > xcrit) # (array([332, 665], dtype=int64),)
拆分索引为:
idx = [0] + list(q[0]+1) + [t.size] # [0, 333, 666, 1001]
主要是标准阈值会受到数据噪声的性质和功率以及两条曲线之间的差距幅度的影响。这种方法的使用取决于在存在噪声的情况下检测曲线间隙的能力。当噪声功率与我们要检测的间隙大小相同时,它就会中断。如果噪声严重拖尾(很少有强异常值),您还可以观察到错误的分裂指数。
在这个 MCVE 中,我们将阈值设置为 20 [Signal Units/Time Units]
:
此 hand-crafted 标准的替代方法是将识别委托给 scipy
的优秀 find_peaks
方法。但它不会避免根据您的信号规格调整检测的要求。
适合origin-shifted数据集
现在我们可以对每个sub-dataset(原点偏移时间)应用曲线拟合,收集参数和统计数据并绘制结果:
trials = []
fig, axe = plt.subplots()
for k, (i, j) in enumerate(zip(idx[:-1], idx[1:])):
p, s = optimize.curve_fit(func, t[i:j]-t[i], xr[i:j])
axe.plot(t[i:j], xr[i:j], '.', label="Data #{}".format(k+1))
axe.plot(t[i:j], func(t[i:j]-t[i], *p), label="Data Fit #{}".format(k+1))
trials.append({"n0": i, "n1": j, "t0": t[i], "a": p[0], "b": p[1],
"s_a": s[0,0], "s_b": s[1,1], "s_ab": s[0,1]})
axe.set_title("Curve Fits")
axe.set_xlabel("Time, $t$")
axe.set_ylabel("Signal Estimate, $\hat{g}(t)$")
axe.legend()
axe.grid()
df = pd.DataFrame(trials)
它returns拟合结果如下:
n0 n1 t0 a b s_a s_b s_ab
0 0 333 0.00 0.998032 -0.199102 0.000011 4.199937e-06 -0.000005
1 333 666 3.33 5.001710 -0.399537 0.000013 3.072542e-07 -0.000002
2 666 1001 6.66 2.002495 -1.203943 0.000030 2.256274e-05 -0.000018
符合我们的原始参数(参见试验数据集部分)。
我们可以通过图形检查拟合优度:
对于单个指数曲线,如图中所示 curve_fit for as single exponential curve , I am able to fit the data using scipy.optimize.curve_fit. However, I am unsure on how to realize a fit for similar dataset composed of multiple exponential curves as shown here double exponential curves。 我使用以下方法实现了对单曲线的拟合:
def exp_decay(x,a,r):
return a * ((1-r)**x)
x = np.linspace(0,50,50)
y = exp_decay(x, 400, 0.06)
y1 = exp_decay(x, 550, 0.06) # this is to be used to append to y to generate two curves
pars, cov = curve_fit(exp_decay, x, y, p0=[0,0])
plt.scatter(x,y)
plt.plot(x, exp_decay(x, *pars), 'r-') #this realizes the fit for a single curve
yx = np.append(y,y1) #this realizes two exponential curves (as shown above - double exponential curves) for which I don't need to fit a model to
有人可以帮助描述如何为两条曲线的数据集实现这一点。我的实际数据集包含多条指数曲线,但我认为如果我能实现两条曲线的拟合,我也许能够为我的数据集复制相同的曲线。 scipy 的 curve_fit 不能这样做;任何有效的实现都很好。
请帮忙!!!
您的问题可以通过使用简单的标准(例如一阶导数估计)拆分数据集轻松解决,然后我们可以对每个子数据集应用简单的曲线拟合程序。
试用数据集
首先,让我们导入一些包并创建一个包含三个曲线的合成数据集来表示您的问题。
我们使用两个参数的指数模型,因为时间原点偏移将由拆分方法处理。我们还添加了噪声,因为真实世界的数据总是存在噪声:
import numpy as np
import pandas as pd
from scipy import optimize
import matplotlib.pyplot as plt
def func(x, a, b):
return a*np.exp(b*x)
N = 1001
n1 = N//3
n2 = 2*n1
t = np.linspace(0, 10, N)
x0 = func(t[:n1], 1, -0.2)
x1 = func(t[n1:n2]-t[n1], 5, -0.4)
x2 = func(t[n2:]-t[n2], 2, -1.2)
x = np.hstack([x0, x1, x2])
xr = x + 0.025*np.random.randn(x.size)
图形呈现如下:
数据集拆分
我们可以使用一个简单的标准将数据集分成三个 sub-datasets 作为一阶导数估计,使用一阶差分来评估它。目标是检测曲线何时急剧上升或下降(数据集应在何处拆分。一阶导数估计如下):
dxrdt = np.abs(np.diff(xr)/np.diff(t))
标准需要一个额外的参数(阈值),必须根据您的信号规格进行调整。该标准相当于:
xcrit = 20
q = np.where(dxrdt > xcrit) # (array([332, 665], dtype=int64),)
拆分索引为:
idx = [0] + list(q[0]+1) + [t.size] # [0, 333, 666, 1001]
主要是标准阈值会受到数据噪声的性质和功率以及两条曲线之间的差距幅度的影响。这种方法的使用取决于在存在噪声的情况下检测曲线间隙的能力。当噪声功率与我们要检测的间隙大小相同时,它就会中断。如果噪声严重拖尾(很少有强异常值),您还可以观察到错误的分裂指数。
在这个 MCVE 中,我们将阈值设置为 20 [Signal Units/Time Units]
:
此 hand-crafted 标准的替代方法是将识别委托给 scipy
的优秀 find_peaks
方法。但它不会避免根据您的信号规格调整检测的要求。
适合origin-shifted数据集
现在我们可以对每个sub-dataset(原点偏移时间)应用曲线拟合,收集参数和统计数据并绘制结果:
trials = []
fig, axe = plt.subplots()
for k, (i, j) in enumerate(zip(idx[:-1], idx[1:])):
p, s = optimize.curve_fit(func, t[i:j]-t[i], xr[i:j])
axe.plot(t[i:j], xr[i:j], '.', label="Data #{}".format(k+1))
axe.plot(t[i:j], func(t[i:j]-t[i], *p), label="Data Fit #{}".format(k+1))
trials.append({"n0": i, "n1": j, "t0": t[i], "a": p[0], "b": p[1],
"s_a": s[0,0], "s_b": s[1,1], "s_ab": s[0,1]})
axe.set_title("Curve Fits")
axe.set_xlabel("Time, $t$")
axe.set_ylabel("Signal Estimate, $\hat{g}(t)$")
axe.legend()
axe.grid()
df = pd.DataFrame(trials)
它returns拟合结果如下:
n0 n1 t0 a b s_a s_b s_ab
0 0 333 0.00 0.998032 -0.199102 0.000011 4.199937e-06 -0.000005
1 333 666 3.33 5.001710 -0.399537 0.000013 3.072542e-07 -0.000002
2 666 1001 6.66 2.002495 -1.203943 0.000030 2.256274e-05 -0.000018
符合我们的原始参数(参见试验数据集部分)。
我们可以通过图形检查拟合优度: