J'ai un ensemble de données (déplacement en fonction du temps) que j'ai ajusté à quelques équations en utilisant la méthode optimise.leastsq. Je cherche maintenant à obtenir des valeurs d'erreur sur les paramètres ajustés. En parcourant la documentation, la matrice produite est la matrice jacobienne, et je dois la multiplier par la matrice résiduelle pour obtenir mes valeurs. Malheureusement je ne suis pas statisticien donc je me noie un peu dans la terminologie.
D'après ce que je comprends, tout ce dont j'ai besoin est la matrice de covariance qui va avec mes paramètres ajustés, donc je peux enraciner les éléments diagonaux pour obtenir mon erreur standard sur les paramètres ajustés. J'ai un vague souvenir de lecture que la matrice de covariance est de toute façon la sortie de la méthode optimise.leastsq. Est-ce correct? Sinon, comment feriez-vous pour obtenir la matrice résiduelle pour multiplier le jacobien produit pour obtenir ma matrice de covariance?
Toute aide serait grandement appréciée. Je suis très nouveau à python et je m'excuse donc si la question s'avère être une question de base.
le code de montage est le suivant:
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)
Les arguments t et disp sont et un tableau de valeurs de temps et de déplacement (essentiellement 2 colonnes de données). J'ai importé tout le nécessaire en haut du code. Les valeurs ajustées et la matrice fournies par la sortie sont les suivantes:
[ 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]]
Je soupçonne que l'ajustement est de toute façon un peu suspect pour le moment. Cela sera confirmé lorsque je pourrai éliminer les erreurs.
Mis à jour le 4/6/2016
Pensons à ajuster une fonction y=f(x)
pour laquelle vous avez un ensemble de points de données (x_i, y_i, yerr_i)
, Où i
est un index qui parcourt chacun de vos points de données.
Dans la plupart des mesures physiques, l'erreur yerr_i
Est une incertitude systématique de l'appareil ou de la procédure de mesure, et elle peut donc être considérée comme une constante qui ne dépend pas de i
.
La méthode optimize.leastsq
Renverra la matrice de covariance fractionnaire. En multipliant tous les éléments de cette matrice par la variance résiduelle (c'est-à-dire le chi carré réduit) et en prenant la racine carrée des éléments diagonaux, vous obtiendrez une estimation de l'écart-type des paramètres d'ajustement. J'ai inclus le code pour le faire dans l'une des fonctions ci-dessous.
D'un autre côté, si vous utilisez optimize.curvefit
, La première partie de la procédure ci-dessus (multipliant par le chi réduit au carré) est faite pour vous en arrière-plan. Vous devez ensuite prendre la racine carrée des éléments diagonaux de la matrice de covariance pour obtenir une estimation de l'écart-type des paramètres d'ajustement.
De plus, optimize.curvefit
Fournit des paramètres facultatifs pour traiter des cas plus généraux, où la valeur yerr_i
Est différente pour chaque point de données. De la documentation :
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.
La détermination d'une estimation correcte de l'erreur standard dans les paramètres ajustés est un problème statistique compliqué. Les résultats de la matrice de covariance, telle qu'implémentée par optimize.curvefit
Et optimize.leastsq
Reposent en fait sur des hypothèses concernant la distribution de probabilité des erreurs et les interactions entre les paramètres; les interactions qui peuvent exister, selon votre fonction d'ajustement spécifique f(x)
.
À mon avis, la meilleure façon de traiter une f(x)
compliquée est d'utiliser la méthode bootstrap, décrite dans ce lien .
Tout d'abord, un code passe-partout. Définissons une fonction de ligne ondulée et générons des données avec des erreurs aléatoires. Nous allons générer un ensemble de données avec une petite erreur aléatoire.
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')
Maintenant, ajustons la fonction en utilisant les différentes méthodes disponibles:
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]
Nous commençons déjà à voir quelque chose d'intéressant, les paramètres et les estimations d'erreur pour les trois méthodes sont presque d'accord. Ça c'est bon!
Supposons maintenant que nous voulions dire aux fonctions d'ajustement qu'il existe une autre incertitude dans nos données, peut-être une incertitude systématique qui contribuerait à une erreur supplémentaire de vingt fois la valeur de err_stdev
. C'est beaucoup d'erreur, en fait, si nous simulons certaines données avec ce type d'erreur, cela ressemblerait à ceci:
Il n'y a certainement aucun espoir que nous pourrions récupérer les paramètres d'ajustement avec ce niveau de bruit.
Pour commencer, réalisons que leastsq
ne nous permet même pas de saisir cette nouvelle information d'erreur systématique. Voyons ce que fait curve_fit
Lorsque nous lui parlons de l'erreur:
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]
Quoi? Cela doit certainement être faux!
C'était la fin de l'histoire, mais récemment curve_fit
A ajouté le paramètre facultatif 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]
C'est un peu mieux, mais toujours un peu louche. curve_fit
Pense que nous pouvons obtenir un ajustement de ce signal bruyant, avec un niveau d'erreur de 10% dans le paramètre p1
. Voyons ce que bootstrap
a à dire:
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 ]
Ah, c'est peut-être une meilleure estimation de l'erreur dans notre paramètre d'ajustement. bootstrap
pense qu'il connaît p1
avec une incertitude d'environ 34%.
optimize.leastsq
Et optimize.curvefit
Nous fournissent un moyen d'estimer les erreurs dans les paramètres ajustés, mais nous ne pouvons pas simplement utiliser ces méthodes sans les remettre en question un peu. Le bootstrap
est une méthode statistique qui utilise la force brute, et à mon avis, elle a tendance à mieux fonctionner dans des situations qui peuvent être plus difficiles à interpréter.
Je recommande fortement de regarder un problème particulier et d'essayer curvefit
et bootstrap
. S'ils sont similaires, alors curvefit
est beaucoup moins cher à calculer, donc vaut probablement la peine d'être utilisé. S'ils diffèrent considérablement, mon argent serait sur le bootstrap
.
J'ai trouvé votre question en essayant de répondre à ma propre question question . Réponse courte. Le cov_x
que les sorties des moindres carrés devraient être multipliées par la variance résiduelle. c'est à dire.
s_sq = (func(popt, args)**2).sum()/(len(ydata)-len(p0))
pcov = pcov * s_sq
un péché curve_fit.py
. En effet, lesssq génère la matrice de covariance fractionnaire. Mon gros problème était que la variance résiduelle apparaît comme autre chose lors de la recherche sur Google.
La variance résiduelle est simplement réduite du chi carré de votre ajustement.
Il est possible de calculer exactement les erreurs en cas de régression linéaire. Et en effet, la fonction lesssq donne des valeurs différentes:
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()
sortie:
[1.3481681543925064, 2.1729338701374137, 0.0036028493647274687, 0.0062446292528624348]
Calculated sigma = 0.237624 # quite close to yerr
[ 1.34816815 2.17293387 0.00360534 0.01907908]
Il est intéressant de savoir quels résultats curvefit et bootstrap donnera ...