Implementation of curve-fitting in Python using curve_fit
from the optimize
sub-module of scipy
.
In this notebook curve_fit
is used to fit a non-linear model, but it also works on linear models.
In the case of linear models you do not need to specify initial estimates of the fit parameters.
Marty Ligare, August 2020
import numpy as np
from scipy import optimize
import urllib # Use if importing data or figures from a URL
import matplotlib as mpl
import matplotlib.pyplot as plt
# Following is an Ipython magic command that puts figures in the notebook.
%matplotlib notebook
# M.L. modification of matplotlib defaults
# Changes can also be put in matplotlibrc file,
# or effected using mpl.rcParams[]
mpl.style.use('classic')
plt.rc('figure', figsize = (6, 4.5)) # Reduces overall size of figures
plt.rc('axes', labelsize=16, titlesize=14)
plt.rc('figure', autolayout = True) # Adjusts supblot parameters for new size
def fun(x, a, b, c, d):
'''Gaussian fit function'''
return a*np.exp(-(x-b)**2/2/c**2) + d
# If data file is local, use:
#data = np.loadtxt("sample2.dat") # Each line in file corresponds to
# single data point: x,y,u
#OR load data from PHYS 310 web page:
link = 'http://www.eg.bucknell.edu/~phys310/jupyter/sample2.dat'
f = urllib.request.urlopen(link)
data = np.loadtxt(f)
x = data.T[0] # The .T gives transpose of array
y = data.T[1]
u = data.T[2]
# More "pythonic" reading of data
# The "unpack = True" reads columns.
link = 'http://www.eg.bucknell.edu/~phys310/jupyter/sample2.dat'
f = urllib.request.urlopen(link)
x, y, u = np.loadtxt(f, unpack=True)
# "quasi-continuous" set of x's for plotting of function:
plt.figure()
plt.xlabel('$x$')
plt.ylabel('$y$')
plt.title('Data')
plt.axhline(0, color='magenta')
# Pad x-range on plot:
plt.xlim(min(x) - 0.05*(max(x) - min(x)), max(x) + 0.05*(max(x) - min(x)))
plt.errorbar(x, y, yerr=u, fmt='o');
p0 = 3.5, 105., 8, 0.2
plt.figure()
# "quasi-continuous" set of x's for plotting of function:
xfine = np.linspace(min(x), max(x), 201)
plt.errorbar(x, y, yerr=u, fmt='o')
plt.plot(xfine, fun(xfine, *p0))
plt.xlabel('$x$')
plt.ylabel('$y$')
plt.title('Data with initial "guesses"')
plt.axhline(0, color='magenta')
# Pad x-range on plot:
plt.xlim(min(x) - 0.05*(max(x) - min(x)), max(x) + 0.05*(max(x) - min(x)));
popt, pcov = optimize.curve_fit(fun, x, y, p0, sigma=u)
plt.figure()
# "quasi-continuous" set of x's for plotting of function:
xfine = np.linspace(min(x), max(x), 201)
plt.xlabel('$x$')
plt.ylabel('$y$')
plt.title('Data with best fit')
plt.axhline(0, color='magenta')
# Pad x-range on plot:
plt.xlim(min(x) - 0.05*(max(x) - min(x)), max(x) + 0.05*(max(x) - min(x)))
plt.errorbar(x, y, yerr=u, fmt='o')
plt.plot(xfine, fun(xfine, *popt));
popt # Best fit parameters
pcov # Covariance matrix
for i in range(len(popt)):
print("parameter", i,"=", popt[i], "+/-", np.sqrt(pcov[i,i]))
NOTE:
absolute_sigma=True
is equivalent to Mathematica VarianceEstimatorFunction-> (1&)
.
Fals
e gives covariance matrix based on estimated errors in data (weights are just relative).
popt, pcov2 = optimize.curve_fit(fun, x, y, p0, sigma=u, absolute_sigma=True)
for i in range(len(popt)):
print("parameter", i,"=", popt[i], "+/-", np.sqrt(pcov2[i,i]))
plt.figure()
r = (fun(x, *popt) - y)/u # calculate residuals
plt.scatter(x,r)
plt.axhline(0, color='magenta')
plt.title('normalized residuals')
plt.xlabel('$x$')
plt.ylabel('$y$')
plt.grid();
Calculation of reduced chi-square parameter:
\begin{equation} \chi_R^2= \frac{1}{N-c}\times\sum_{i=1}^N \frac{(y_i-f(x_i))^2}{\sigma_i^2}, \end{equation}np.sum((y - fun(x, *popt))**2/u**2)/(len(data) - 4)
version_information
is from J.R. Johansson (jrjohansson at gmail.com); see Introduction to scientific computing with Python for more information and instructions for package installation.
version_information
is installed on the linux network at Bucknell
%load_ext version_information
%version_information numpy, scipy, matplotlib