Example of fitting a non-linear model. This notebook uses curve_fit()
from the optimize
submodule of scipy
. (This function can be used to fit linear and non-linear models. When used
with a non-linear model, as in this notebook, initial estimates of the fit parameters are required
in order to converge on the desired minimum in $\chi^2$ space.)
Marty Ligare, August 2020
modified by Katharina Vollmayr-Lee, February 2022
import numpy as np
from scipy import optimize
import urllib
import matplotlib as mpl
import matplotlib.pyplot as plt
# Following is an Ipython magic command that puts figures in notebook.
%matplotlib notebook
# M.L. modifications 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 params for new size
This function is parametrized in terms of the wavelength wl
and not the wavenumber.
It's easy to estimate the wavelength by simple inspection of the graph, without
additional calculation.
def f(x,a,wl,ph):
'''Cosine function with three fit parameters: a (amplitude), wl (wavelength),
and ph (phase)'''
return a*np.cos(2*np.pi*x/wl + ph)
We could download the data file to a local directory, and import it using np.loadtxt
, but
let's skip the intermediate step and load it directly to the notebook.
Each row in file gives information on single data point, $x_i$, $y_i$, $\alpha_i$. The option
unpack = True
takes transpose of the data file array so that all values of x
are in a single
array, and similarly for y
and u
(the uncertainty). Notice that by default np.loadtxt()
treats
#
as the comment character.
link = 'http://www.eg.bucknell.edu/~phys310/skills/data_analysis/b.dat'
fh = urllib.request.urlopen(link)
xdata, ydata, udata = np.loadtxt(fh, unpack=True)
print(xdata)
Since for most labs you will have a file with your data instead of the file of a URL link, let us practice here how to use np.loadtext
directly on a file. So download from the Phys 310 webpage the file a.dat
into your local directory. Next we import it using np.loadtxt
.
Each row in this file gives information on single data point, $x_i$, $y_i$, $\alpha_i$. The option
unpack = True
takes transpose of the data file array so that all values of x
are in a single
array, and similarly for y
and u
(the uncertainty). Notice that by default np.loadtxt()
treats
#
as the comment character.
xdata, ydata, udata = np.loadtxt("b.dat", unpack=True)
#print(xdata,ydata,udata)
plt.figure()
plt.errorbar(xdata,ydata,udata, fmt='ok')
plt.xlabel('$x$')
plt.ylabel('$y$');
a
), wavelength (wl
), and phase (ph
)¶Required for fitting of non-linear model
p0 = 1.2, 1.7, 0 # a, wl, ph estimates
print(p0)
plt.figure()
plt.errorbar(xdata,ydata,udata, fmt='ok')
xc = np.linspace(0,4,100)
#plt.plot(xc, f(xc,1.2,1.7,0));
plt.plot(xc, f(xc,*p0))
popt, pcov = optimize.curve_fit(f,xdata,ydata,p0, sigma=udata, absolute_sigma=True)
print(popt)
for i in range(len(popt)):
print(np.sqrt(pcov[i,i]))
plt.figure()
plt.errorbar(xdata,ydata,udata, fmt='ok')
xc = np.linspace(0,4,100)
plt.plot(xc, f(xc,*popt));
r = ydata - f(xdata,*popt)
plt.figure()
plt.errorbar(xdata,r,udata, fmt='ko')
plt.axhline(0)
plt.xlabel('$x$')
plt.ylabel('residuals ($y_i - f(x_i)$)')
plt.title('Residuals');
chisq = np.sum(r**2/udata**2)
print(chisq, len(r))
The value of $\chi^2$ is approximately equal to the number of data points, suggesting the data is consistent with the model.
%version_information is an IPython magic extension for showing version information for dependency modules in a notebook;
See https://github.com/jrjohansson/version_information
%version_information
is available on Bucknell computers on the linux network. You can easily install it on
any computer.
%load_ext version_information
version_information numpy, scipy, matplotlib