Optimize.leastsqメソッドを使用していくつかの方程式に適合させたデータのセット(変位対時間)があります。私は現在、適合パラメータのエラー値を取得しようとしています。ドキュメントを見ると、出力されるマトリックスはヤコビアンマトリックスであり、これに残差マトリックスを掛けて値を取得する必要があります。残念ながら私は統計学者ではないので、専門用語に多少drれています。
私が必要とするのは、適合パラメーターに伴う共分散行列だけであるため、対角要素の平方根を作成して適合パラメーターの標準誤差を取得できます。とにかく最適化.leastsqメソッドから出力されるのは共分散行列であると読むことの漠然とした記憶があります。これは正しいです?そうでない場合、残差行列を取得して出力されたヤコビ行列を乗算して共分散行列を取得しますか?
どんな助けも大歓迎です。 pythonが初めてなので、質問が基本的なものであることが判明した場合は謝罪します。
フィッティングコードは次のとおりです。
fitfunc = lambda p, t: p[0]+p[1]*np.log(t-p[2])+ p[3]*t # Target function'
errfunc = lambda p, t, y: (fitfunc(p, t) - y)# Distance to the target function
p0 = [ 1,1,1,1] # Initial guess for the parameters
out = optimize.leastsq(errfunc, p0[:], args=(t, disp,), full_output=1)
Args tおよびdisp isおよびtimeおよびdisplcement値の配列(基本的には2列のデータ)。コードの頂点で必要なものをすべてインポートしました。出力によって提供される近似値とマトリックスは次のとおりです。
[ 7.53847074e-07 1.84931494e-08 3.25102795e+01 -3.28882437e-11]
[[ 3.29326356e-01 -7.43957919e-02 8.02246944e+07 2.64522183e-04]
[ -7.43957919e-02 1.70872763e-02 -1.76477289e+07 -6.35825520e-05]
[ 8.02246944e+07 -1.76477289e+07 2.51023348e+16 5.87705672e+04]
[ 2.64522183e-04 -6.35825520e-05 5.87705672e+04 2.70249488e-07]]
とにかく、現時点ではフィットが少し疑わしいと思います。これは、エラーが出たときに確認されます。
2016年4月6日に更新
データポイントのセット_(x_i, y_i, yerr_i)
_を持つ関数y=f(x)
のフィッティングについて考えてみましょう。ここで、i
は各データポイントに対して実行されるインデックスです。
ほとんどの物理測定では、エラー_yerr_i
_は測定デバイスまたは手順の体系的な不確実性であるため、i
に依存しない定数と考えることができます。
_optimize.leastsq
_メソッドは、分数共分散行列を返します。この行列のすべての要素に残差分散(つまり、縮小カイ2乗)を乗算し、対角要素の平方根を取得すると、近似パラメーターの標準偏差の推定値が得られます。それを行うためのコードを以下の関数の1つに含めました。
一方、_optimize.curvefit
_を使用する場合は、上記の手順の最初の部分(縮小カイ2乗による乗算)が舞台裏で行われます。次に、共分散行列の対角要素の平方根を取得して、近似パラメーターの標準偏差の推定値を取得する必要があります。
さらに、_optimize.curvefit
_は、_yerr_i
_値がデータポイントごとに異なる、より一般的なケースに対処するオプションのパラメーターを提供します。 ドキュメント から:
_sigma : None or M-length sequence, optional
If not None, the uncertainties in the ydata array. These are used as
weights in the least-squares problem
i.e. minimising ``np.sum( ((f(xdata, *popt) - ydata) / sigma)**2 )``
If None, the uncertainties are assumed to be 1.
absolute_sigma : bool, optional
If False, `sigma` denotes relative weights of the data points.
The returned covariance matrix `pcov` is based on *estimated*
errors in the data, and is not affected by the overall
magnitude of the values in `sigma`. Only the relative
magnitudes of the `sigma` values matter.
_
近似パラメーターの標準誤差の適切な推定値を決定することは、複雑な統計上の問題です。 _optimize.curvefit
_および_optimize.leastsq
_によって実装される共分散行列の結果は、実際にはエラーの確率分布とパラメーター間の相互作用に関する仮定に依存しています。特定の適合関数f(x)
に応じて、存在する可能性のある相互作用。
私の意見では、複雑なf(x)
を処理する最良の方法は、bootstrap=メソッドを使用することです。これは this link で概説されています。
まず、いくつかの定型コード。波線関数を定義して、ランダムエラーのあるデータを生成してみましょう。小さなランダムエラーのあるデータセットを生成します。
_import numpy as np
from scipy import optimize
import random
def f( x, p0, p1, p2):
return p0*x + 0.4*np.sin(p1*x) + p2
def ff(x, p):
return f(x, *p)
# These are the true parameters
p0 = 1.0
p1 = 40
p2 = 2.0
# These are initial guesses for fits:
pstart = [
p0 + random.random(),
p1 + 5.*random.random(),
p2 + random.random()
]
%matplotlib inline
import matplotlib.pyplot as plt
xvals = np.linspace(0., 1, 120)
yvals = f(xvals, p0, p1, p2)
# Generate data with a bit of randomness
# (the noise-less function that underlies the data is shown as a blue line)
xdata = np.array(xvals)
np.random.seed(42)
err_stdev = 0.2
yvals_err = np.random.normal(0., err_stdev, len(xdata))
ydata = f(xdata, p0, p1, p2) + yvals_err
plt.plot(xvals, yvals)
plt.plot(xdata, ydata, 'o', mfc='None')
_
次に、使用可能なさまざまなメソッドを使用して関数を適合させましょう。
_def fit_leastsq(p0, datax, datay, function):
errfunc = lambda p, x, y: function(x,p) - y
pfit, pcov, infodict, errmsg, success = \
optimize.leastsq(errfunc, p0, args=(datax, datay), \
full_output=1, epsfcn=0.0001)
if (len(datay) > len(p0)) and pcov is not None:
s_sq = (errfunc(pfit, datax, datay)**2).sum()/(len(datay)-len(p0))
pcov = pcov * s_sq
else:
pcov = np.inf
error = []
for i in range(len(pfit)):
try:
error.append(np.absolute(pcov[i][i])**0.5)
except:
error.append( 0.00 )
pfit_leastsq = pfit
perr_leastsq = np.array(error)
return pfit_leastsq, perr_leastsq
pfit, perr = fit_leastsq(pstart, xdata, ydata, ff)
print("\n# Fit parameters and parameter errors from lestsq method :")
print("pfit = ", pfit)
print("perr = ", perr)
_
_# Fit parameters and parameter errors from lestsq method :
pfit = [ 1.04951642 39.98832634 1.95947613]
perr = [ 0.0584024 0.10597135 0.03376631]
_
_def fit_curvefit(p0, datax, datay, function, yerr=err_stdev, **kwargs):
"""
Note: As per the current documentation (Scipy V1.1.0), sigma (yerr) must be:
None or M-length sequence or MxM array, optional
Therefore, replace:
err_stdev = 0.2
With:
err_stdev = [0.2 for item in xdata]
Or similar, to create an M-length sequence for this example.
"""
pfit, pcov = \
optimize.curve_fit(f,datax,datay,p0=p0,\
sigma=yerr, epsfcn=0.0001, **kwargs)
error = []
for i in range(len(pfit)):
try:
error.append(np.absolute(pcov[i][i])**0.5)
except:
error.append( 0.00 )
pfit_curvefit = pfit
perr_curvefit = np.array(error)
return pfit_curvefit, perr_curvefit
pfit, perr = fit_curvefit(pstart, xdata, ydata, ff)
print("\n# Fit parameters and parameter errors from curve_fit method :")
print("pfit = ", pfit)
print("perr = ", perr)
_
_# Fit parameters and parameter errors from curve_fit method :
pfit = [ 1.04951642 39.98832634 1.95947613]
perr = [ 0.0584024 0.10597135 0.03376631]
_
_def fit_bootstrap(p0, datax, datay, function, yerr_systematic=0.0):
errfunc = lambda p, x, y: function(x,p) - y
# Fit first time
pfit, perr = optimize.leastsq(errfunc, p0, args=(datax, datay), full_output=0)
# Get the stdev of the residuals
residuals = errfunc(pfit, datax, datay)
sigma_res = np.std(residuals)
sigma_err_total = np.sqrt(sigma_res**2 + yerr_systematic**2)
# 100 random data sets are generated and fitted
ps = []
for i in range(100):
randomDelta = np.random.normal(0., sigma_err_total, len(datay))
randomdataY = datay + randomDelta
randomfit, randomcov = \
optimize.leastsq(errfunc, p0, args=(datax, randomdataY),\
full_output=0)
ps.append(randomfit)
ps = np.array(ps)
mean_pfit = np.mean(ps,0)
# You can choose the confidence interval that you want for your
# parameter estimates:
Nsigma = 1. # 1sigma gets approximately the same as methods above
# 1sigma corresponds to 68.3% confidence interval
# 2sigma corresponds to 95.44% confidence interval
err_pfit = Nsigma * np.std(ps,0)
pfit_bootstrap = mean_pfit
perr_bootstrap = err_pfit
return pfit_bootstrap, perr_bootstrap
pfit, perr = fit_bootstrap(pstart, xdata, ydata, ff)
print("\n# Fit parameters and parameter errors from bootstrap method :")
print("pfit = ", pfit)
print("perr = ", perr)
_
_# Fit parameters and parameter errors from bootstrap method :
pfit = [ 1.05058465 39.96530055 1.96074046]
perr = [ 0.06462981 0.1118803 0.03544364]
_
私たちはすでに興味深いものを見始めています。3つの方法すべてのパラメーターと誤差の推定値はほぼ一致しています。それはいいです!
ここで、データに他の不確実性、おそらく_err_stdev
_の値の20倍の追加誤差をもたらす系統的な不確実性があることをフィッティング関数に伝えたいとします。それは、大量のエラーです。実際、この種のエラーでデータをシミュレートすると、次のようになります。
確かに、このレベルのノイズで適合パラメーターを回復できる見込みはありません。
まず、leastsq
ではこの新しい体系的なエラー情報を入力することさえできないことを理解しましょう。エラーについて伝えるときに_curve_fit
_が何をするのか見てみましょう:
_pfit, perr = fit_curvefit(pstart, xdata, ydata, ff, yerr=20*err_stdev)
print("\nFit parameters and parameter errors from curve_fit method (20x error) :")
print("pfit = ", pfit)
print("perr = ", perr)
_
_Fit parameters and parameter errors from curve_fit method (20x error) :
pfit = [ 1.04951642 39.98832633 1.95947613]
perr = [ 0.0584024 0.10597135 0.03376631]
_
え?これは確かに間違っているに違いありません!
これでストーリーは終わりましたが、最近_curve_fit
_が_absolute_sigma
_オプションパラメータを追加しました:
_pfit, perr = fit_curvefit(pstart, xdata, ydata, ff, yerr=20*err_stdev, absolute_sigma=True)
print("\n# Fit parameters and parameter errors from curve_fit method (20x error, absolute_sigma) :")
print("pfit = ", pfit)
print("perr = ", perr)
_
_# Fit parameters and parameter errors from curve_fit method (20x error, absolute_sigma) :
pfit = [ 1.04951642 39.98832633 1.95947613]
perr = [ 1.25570187 2.27847504 0.72600466]
_
それは幾分良いですが、それでも少し怪しいです。 _curve_fit
_は、_p1
_パラメーターのレベルが10%のエラーで、そのノイズの多い信号から適合させることができると考えています。 bootstrap
が何を言っているのか見てみましょう:
_pfit, perr = fit_bootstrap(pstart, xdata, ydata, ff, yerr_systematic=20.0)
print("\nFit parameters and parameter errors from bootstrap method (20x error):")
print("pfit = ", pfit)
print("perr = ", perr)
_
_Fit parameters and parameter errors from bootstrap method (20x error):
pfit = [ 2.54029171e-02 3.84313695e+01 2.55729825e+00]
perr = [ 6.41602813 13.22283345 3.6629705 ]
_
ああ、それはおそらく適合パラメータの誤差のより良い推定です。 bootstrap
は、_p1
_を約34%の不確実性で認識していると考えています。
_optimize.leastsq
_および_optimize.curvefit
_は、近似パラメーターのエラーを推定する方法を提供しますが、少し質問せずにこれらのメソッドを使用することはできません。 bootstrap
はブルートフォースを使用する統計的手法であり、私の意見では、解釈が難しい状況ではよりうまく機能する傾向があります。
特定の問題を見て、curvefit
とbootstrap
を試すことを強くお勧めします。それらが類似している場合、curvefit
の計算ははるかに安価なので、おそらく使用する価値があります。それらが大幅に異なる場合、私のお金はbootstrap
にあります。
私自身の同様の question に答えようとしている間にあなたの質問を見つけました。短い答え。 leastsqが出力するcov_x
に残差分散を掛ける必要があります。つまり.
s_sq = (func(popt, args)**2).sum()/(len(ydata)-len(p0))
pcov = pcov * s_sq
curve_fit.py
のように。これは、leastsqが分数共分散行列を出力するためです。私の大きな問題は、残差分散をグーグルで調べたときに他の何かとして現れることでした。
残差分散は、近似からカイ二乗を単純に削減したものです。
線形回帰の場合、エラーを正確に計算することができます。そして、実際、leastsq関数は異なる値を提供します。
import numpy as np
from scipy.optimize import leastsq
import matplotlib.pyplot as plt
A = 1.353
B = 2.145
yerr = 0.25
xs = np.linspace( 2, 8, 1448 )
ys = A * xs + B + np.random.normal( 0, yerr, len( xs ) )
def linearRegression( _xs, _ys ):
if _xs.shape[0] != _ys.shape[0]:
raise ValueError( 'xs and ys must be of the same length' )
xSum = ySum = xxSum = yySum = xySum = 0.0
numPts = 0
for i in range( len( _xs ) ):
xSum += _xs[i]
ySum += _ys[i]
xxSum += _xs[i] * _xs[i]
yySum += _ys[i] * _ys[i]
xySum += _xs[i] * _ys[i]
numPts += 1
k = ( xySum - xSum * ySum / numPts ) / ( xxSum - xSum * xSum / numPts )
b = ( ySum - k * xSum ) / numPts
sk = np.sqrt( ( ( yySum - ySum * ySum / numPts ) / ( xxSum - xSum * xSum / numPts ) - k**2 ) / numPts )
sb = np.sqrt( ( yySum - ySum * ySum / numPts ) - k**2 * ( xxSum - xSum * xSum / numPts ) ) / numPts
return [ k, b, sk, sb ]
def linearRegressionSP( _xs, _ys ):
defPars = [ 0, 0 ]
pars, pcov, infodict, errmsg, success = \
leastsq( lambda _pars, x, y: y - ( _pars[0] * x + _pars[1] ), defPars, args = ( _xs, _ys ), full_output=1 )
errs = []
if pcov is not None:
if( len( _xs ) > len(defPars) ) and pcov is not None:
s_sq = ( ( ys - ( pars[0] * _xs + pars[1] ) )**2 ).sum() / ( len( _xs ) - len( defPars ) )
pcov *= s_sq
for j in range( len( pcov ) ):
errs.append( pcov[j][j]**0.5 )
else:
errs = [ np.inf, np.inf ]
return np.append( pars, np.array( errs ) )
regr1 = linearRegression( xs, ys )
regr2 = linearRegressionSP( xs, ys )
print( regr1 )
print( 'Calculated sigma = %f' % ( regr1[3] * np.sqrt( xs.shape[0] ) ) )
print( regr2 )
#print( 'B = %f must be in ( %f,\t%f )' % ( B, regr1[1] - regr1[3], regr1[1] + regr1[3] ) )
plt.plot( xs, ys, 'bo' )
plt.plot( xs, regr1[0] * xs + regr1[1] )
plt.show()
出力:
[1.3481681543925064, 2.1729338701374137, 0.0036028493647274687, 0.0062446292528624348]
Calculated sigma = 0.237624 # quite close to yerr
[ 1.34816815 2.17293387 0.00360534 0.01907908]
Curvefitとbootstrapが与える結果は...