I was reviewing Getting standard errors on fitted parameters using the optimize.leastsq method in python as I want to use optimize.least_squares to fit some data, however I have bounds (for physical reasons) on my parameters, e.g. parameter A must be [0,1]. I see that the method using the covariance matrix and multiplying by the residuals leads to errorbars that extend outside the bounds of the parameters. What is the statistically correct and rigorous thing to do? Artificially cut off my errorbars at my permitted bounds or is there some other way to take care of the errorbars to be within the boundaries?
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 #In the fit we will bound p0 from 0 to 1 for e.g., some physical/modeling reason
p1 = 40
p2 = 2.0
# These are initial guesses for fits:
pstart = [0.8,40,2]
# Generate data with a bit of randomness
# (the noise-less function that underlies the data is shown as a blue line)
xdata = np.array(np.linspace(0., 1, 120))
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
def fit_leastsq(p0, datax, datay, function, bounds1):
errfunc = lambda p, x, y: function(x,p) - y
all_results= optimize.least_squares(errfunc, p0, args=(datax, datay),bounds=bounds1 )
pfit=all_results.x
J=final_results.jac #the jacobian matrix
pcov=np.linalg.inv(2*J.T.dot(J)) #This calculates the inverse Hessian which is used to approximate the covariance
s_sq = (errfunc(pfit, datax, datay)**2).sum()/(len(datay)-len(p0))
pcov = pcov * s_sq
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
#Set Bounds on Parameters
bounds=([0,0,0],[1,40,2.5]) #this means first parameter is restricted 0-1, second is 0-40, and this is 0-2.5
pfit, perr = fit_leastsq(pstart, xdata, ydata, ff, bounds)
print("\n# Fit parameters and parameter errors from lestsq method :")
print("pfit = ", pfit)
print("perr = ", perr)
The results I get are:
# Fit parameters and parameter errors from lestsq method :
pfit = [ 1. 39.97612883 1.98430673]
perr = [0.07545234 0.07611141 0.01546065]
Note that the perr on the parameters extends outside the range of the bounds. Perhaps there is a way to get directional error? That is, for example, the errorbar on the first parameter will only be in the negative direction?