指数減衰をデータに適合させることができるscipy/numpyモジュールを知っている人はいますか?
Google検索でいくつかのブログ投稿が返されました。たとえば、 http://exnumerus.blogspot.com/2010/04/how-to-fit-exponential-decay-example-in.html ですが、ソリューションでは、yオフセットを事前に指定する必要がありますが、これは常に可能とは限りません
編集:
curve_fitは機能しますが、パラメーターの初期推定がないため、非常に惨めに失敗する可能性があり、それが必要になる場合があります。私が取り組んでいるコードは
#!/usr/bin/env python
import numpy as np
import scipy as sp
import pylab as pl
from scipy.optimize.minpack import curve_fit
x = np.array([ 50., 110., 170., 230., 290., 350., 410., 470.,
530., 590.])
y = np.array([ 3173., 2391., 1726., 1388., 1057., 786., 598.,
443., 339., 263.])
smoothx = np.linspace(x[0], x[-1], 20)
guess_a, guess_b, guess_c = 4000, -0.005, 100
guess = [guess_a, guess_b, guess_c]
exp_decay = lambda x, A, t, y0: A * np.exp(x * t) + y0
params, cov = curve_fit(exp_decay, x, y, p0=guess)
A, t, y0 = params
print "A = %s\nt = %s\ny0 = %s\n" % (A, t, y0)
pl.clf()
best_fit = lambda x: A * np.exp(t * x) + y0
pl.plot(x, y, 'b.')
pl.plot(smoothx, best_fit(smoothx), 'r-')
pl.show()
これは機能しますが、「p0 = guess」を削除すると、惨めに失敗します。
次の2つのオプションがあります。
scipy.optimize.curve_fit
_最初のオプションは、断然最速で最も堅牢です。ただし、事前にyオフセットを知っている必要があります。そうでない場合、方程式を線形化することはできません。 (つまり、y = A * exp(K * t)
はy = log(A * exp(K * t)) = K * t + log(A)
をフィッティングすることによって線形化できますが、y = A*exp(K*t) + C
はy - C = K*t + log(A)
をフィッティングすることによってのみ線形化でき、y
は独立変数です。これが線形システムであるためには、C
が事前にわかっている必要があります。
非線形の方法を使用する場合、それはa)収束して解を生成することが保証されていない、b)非常に遅くなる、c)パラメータの不確実性の推定がはるかに悪い、そしてd)多くの場合精度がはるかに低い。ただし、非線形の方法には、線形の反転よりも大きな利点が1つあります。非線形の方程式系を解くことができます。あなたの場合、これはあなたが事前にC
を知る必要がないことを意味します。
例を示すために、線形メソッドと非線形メソッドの両方を使用して、いくつかのノイズのあるデータでy = A * exp(K * t)を解いてみましょう。
_import numpy as np
import matplotlib.pyplot as plt
import scipy as sp
import scipy.optimize
def main():
# Actual parameters
A0, K0, C0 = 2.5, -4.0, 2.0
# Generate some data based on these
tmin, tmax = 0, 0.5
num = 20
t = np.linspace(tmin, tmax, num)
y = model_func(t, A0, K0, C0)
# Add noise
noisy_y = y + 0.5 * (np.random.random(num) - 0.5)
fig = plt.figure()
ax1 = fig.add_subplot(2,1,1)
ax2 = fig.add_subplot(2,1,2)
# Non-linear Fit
A, K, C = fit_exp_nonlinear(t, noisy_y)
fit_y = model_func(t, A, K, C)
plot(ax1, t, y, noisy_y, fit_y, (A0, K0, C0), (A, K, C0))
ax1.set_title('Non-linear Fit')
# Linear Fit (Note that we have to provide the y-offset ("C") value!!
A, K = fit_exp_linear(t, y, C0)
fit_y = model_func(t, A, K, C0)
plot(ax2, t, y, noisy_y, fit_y, (A0, K0, C0), (A, K, 0))
ax2.set_title('Linear Fit')
plt.show()
def model_func(t, A, K, C):
return A * np.exp(K * t) + C
def fit_exp_linear(t, y, C=0):
y = y - C
y = np.log(y)
K, A_log = np.polyfit(t, y, 1)
A = np.exp(A_log)
return A, K
def fit_exp_nonlinear(t, y):
opt_parms, parm_cov = sp.optimize.curve_fit(model_func, t, y, maxfev=1000)
A, K, C = opt_parms
return A, K, C
def plot(ax, t, y, noisy_y, fit_y, orig_parms, fit_parms):
A0, K0, C0 = orig_parms
A, K, C = fit_parms
ax.plot(t, y, 'k--',
label='Actual Function:\n $y = %0.2f e^{%0.2f t} + %0.2f$' % (A0, K0, C0))
ax.plot(t, fit_y, 'b-',
label='Fitted Function:\n $y = %0.2f e^{%0.2f t} + %0.2f$' % (A, K, C))
ax.plot(t, noisy_y, 'ro')
ax.legend(bbox_to_anchor=(1.05, 1.1), fancybox=True, shadow=True)
if __name__ == '__main__':
main()
_
線形解では、実際の値により近い結果が得られることに注意してください。ただし、線形ソリューションを使用するには、yオフセット値を指定する必要があります。非線形ソリューションは、この事前の知識を必要としません。
scipy.optimize.curve_fit
関数。そのためのドキュメント文字列には、ここにコピーする指数減衰の例も含まれています。
>>> import numpy as np
>>> from scipy.optimize import curve_fit
>>> def func(x, a, b, c):
... return a*np.exp(-b*x) + c
>>> x = np.linspace(0,4,50)
>>> y = func(x, 2.5, 1.3, 0.5)
>>> yn = y + 0.2*np.random.normal(size=len(x))
>>> popt, pcov = curve_fit(func, x, yn)
ランダムノイズが追加されたため、適合したパラメーターは異なりますが、aにはノイズが含まれているため、2.47990495、1.40709306、0.53753635をa、b、cとして取得しました。 ynではなくyに適合させると、正確なa、b、cの値が得られます。
反復プロセスではなく初期推測なしで指数関数を近似する手順:
これは論文(pp.16-17)に由来します: https://fr.scribd.com/doc/14674814/Regressions-et-equations-integrales
必要に応じて、これを使用して非線形回帰計算を初期化し、最適化の特定の基準を選択できます。
例:
Joe Kingtonの例は興味深いものです。残念ながら、データは表示されず、グラフのみが表示されます。したがって、以下のデータ(x、y)はグラフのグラフィックスキャンから得られたものであり、その結果、数値はおそらくJoe Kingtonによって使用されたものと正確には一致しません。それにもかかわらず、「適合された」曲線のそれぞれの方程式は、点の広いばらつきを考慮して、互いに非常に近いです。
上の図は、Kingtonのグラフのコピーです。
下の図は、上記の手順で得られた結果を示しています。
それを行う正しい方法は、Prony推定を行い、その結果を最小二乗フィッティング(またはその他のよりロバストなフィッティングルーチン)の初期推定として使用することです。 Prony推定では初期推定は必要ありませんが、適切な推定を行うには多くのポイントが必要です。
ここに概要があります
http://www.statsci.org/other/prony.html
Octaveでは、これはexpfit
として実装されているため、Octaveライブラリー関数に基づいて独自のルーチンを作成できます。
Prony推定ではオフセットを知る必要がありますが、減衰に「十分に」進んだ場合、オフセットの妥当な推定値があるため、データをシフトしてオフセットを0に配置できます。いずれにせよ、Prony推定は、他のフィッティングルーチンの妥当な初期推定値を取得する方法にすぎません。
私はpythonを知りませんが、独立した座標に一定の差がある3つのデータポイントが与えられた場合、オフセットを使用して指数関数的減衰の係数を非反復的に推定する簡単な方法を知っています。データポイントの独立した座標には一定の差があるため(x値は60の間隔で配置されます)、私の方法をそれらに適用できます。あなたはきっと数学をPythonに翻訳することができます。
想定する
_y = A + B*exp(-c*x) = A + B*C^x
_
ここで、C = exp(-c)
Y_0、y_1、y_2が与えられた場合、x = 0、1、2に対して、
_y_0 = A + B
y_1 = A + B*C
y_2 = A + B*C^2
_
a、B、Cを次のように検索します。
_A = (y_0*y_2 - y_1^2)/(y_0 + y_2 - 2*y_1)
B = (y_1 - y_0)^2/(y_0 + y_2 - 2*y_1)
C = (y_2 - y_1)/(y_1 - y_0)
_
対応する指数は、3つの点(0、y_0)、(1、y_1)、および(2、y_2)を正確に通過します。データポイントがx座標0、1、2ではなく、k、k + s、およびk + 2 * sにある場合、
_y = A′ + B′*C′^(k + s*x) = A′ + B′*C′^k*(C′^s)^x = A + B*C^x
_
したがって、上記の式を使用してA、B、Cを検索し、計算することができます
_A′ = A
C′ = C^(1/s)
B′ = B/(C′^k)
_
結果の係数は、y座標のエラーに非常に敏感です。使用される3つのデータポイントで定義された範囲を超えて外挿すると、大きなエラーが発生する可能性があるため、可能な限り離れている(ただし、両者の距離は固定されている)。
データセットには10の等距離データポイントがあります。 3つのデータポイント(110、2391)、(350、786)、(590、263)を選択して使用します。これらは、独立座標で最大の固定距離(240)を持っています。したがって、y_0 = 2391、y_1 = 786、y_2 = 263、k = 110、s =240。次に、A = 10.20055、B = 2380.799、C = 0.3258567、A '= 10.20055、B' = 3980.329、C '= 0.9953388。指数は
_y = 10.20055 + 3980.329*0.9953388^x = 10.20055 + 3980.329*exp(-0.004672073*x)
_
この指数は、非線形近似アルゴリズムの初期推定として使用できます。
Aの計算式は、シャンクス変換で使用されるものと同じです( http://en.wikipedia.org/wiki/Shanks_transformation )。
@JJacquelinのソリューションのPython実装。私は初期の推測がなく、おおよその非ソルバーベースのソリューションが必要だったので、@ JJacquelinの答えは本当に役に立ちました。元の質問はpython numpy/scipyリクエストとして提起されました。@ johanvdwのNice clean Rコードを受け取り、それをpython/numpyとしてリファクタリングしました。誰かに役立つと思います: https:/ /Gist.github.com/friendtogeoff/00b89fa8d9acc1b2bdf3bdb675178a29
import numpy as np
"""
compute an exponential decay fit to two vectors of x and y data
result is in form y = a + b * exp(c*x).
ref. https://Gist.github.com/johanvdw/443a820a7f4ffa7e9f8997481d7ca8b3
"""
def exp_est(x,y):
n = np.size(x)
# sort the data into ascending x order
y = y[np.argsort(x)]
x = x[np.argsort(x)]
Sk = np.zeros(n)
for n in range(1,n):
Sk[n] = Sk[n-1] + (y[n] + y[n-1])*(x[n]-x[n-1])/2
dx = x - x[0]
dy = y - y[0]
m1 = np.matrix([[np.sum(dx**2), np.sum(dx*Sk)],
[np.sum(dx*Sk), np.sum(Sk**2)]])
m2 = np.matrix([np.sum(dx*dy), np.sum(dy*Sk)])
[d, c] = (m1.I * m2.T).flat
m3 = np.matrix([[n, np.sum(np.exp( c*x))],
[np.sum(np.exp(c*x)),np.sum(np.exp(2*c*x))]])
m4 = np.matrix([np.sum(y), np.sum(y*np.exp(c*x).T)])
[a, b] = (m3.I * m4.T).flat
return [a,b,c]
何も推測したくないので、curve_fitが正しく機能しませんでした。私はジョー・キントンの例を単純化しようとしていました、そしてこれは私が働いていたものです。アイデアは、「ノイズの多い」データをログに変換してから変換し直し、polyfitとpolyvalを使用してパラメーターを把握することです。
model = np.polyfit(xVals, np.log(yVals) , 1);
splineYs = np.exp(np.polyval(model,xVals[0]));
pyplot.plot(xVals,yVals,','); #show scatter plot of original data
pyplot.plot(xVals,splineYs('b-'); #show fitted line
pyplot.show()
ここで、xValsとyValsは単なるリストです。
減衰が0から開始しない場合:
popt, pcov = curve_fit(self.func, x-x0, y)
ここで、x0は減衰の開始(フィットを開始する場所)。そして、もう一度x0を使ってプロットします:
plt.plot(x, self.func(x-x0, *popt),'--r', label='Fit')
関数は次のとおりです。
def func(self, x, a, tau, c):
return a * np.exp(-x/tau) + c