1021 KiB
1021 KiB
In [1]:
%matplotlib inline
# Common imports
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from IPython.display import display
import os
# Where to save the figures and data files
PROJECT_ROOT_DIR = "Results"
FIGURE_ID = "Results/FigureFiles"
DATA_ID = "DataFiles/"
if not os.path.exists(PROJECT_ROOT_DIR):
os.mkdir(PROJECT_ROOT_DIR)
if not os.path.exists(FIGURE_ID):
os.makedirs(FIGURE_ID)
if not os.path.exists(DATA_ID):
os.makedirs(DATA_ID)
def image_path(fig_id):
return os.path.join(FIGURE_ID, fig_id)
def data_path(dat_id):
return os.path.join(DATA_ID, dat_id)
def save_fig(fig_id):
plt.savefig(image_path(fig_id) + ".png", format='png')
infile = open(data_path("MassEval2016.dat"),'r')
# Read the experimental data with Pandas
Masses = pd.read_fwf(infile, usecols=(2,3,4,6,11),
names=('N', 'Z', 'A', 'Element', 'Ebinding'),
widths=(1,3,5,5,5,1,3,4,1,13,11,11,9,1,2,11,9,1,3,1,12,11,1),
header=39,
index_col=False)
# Extrapolated values are indicated by '#' in place of the decimal place, so
# the Ebinding column won't be numeric. Coerce to float and drop these entries.
Masses['Ebinding'] = pd.to_numeric(Masses['Ebinding'], errors='coerce')
Masses = Masses.dropna()
# Convert from keV to MeV.
Masses['Ebinding'] /= 1000
# Group the DataFrame by nucleon number, A.
Masses = Masses.groupby('A')
# Find the rows of the grouped DataFrame with the maximum binding energy.
Masses = Masses.apply(lambda t: t[t.Ebinding==t.Ebinding.max()])
A = Masses['A']
Z = Masses['Z']
N = Masses['N']
Element = Masses['Element']
Energies = Masses['Ebinding']
# Now we set up the design matrix X
X = np.zeros((len(A),5))
X[:,0] = 1
X[:,1] = A
X[:,2] = A**(2.0/3.0)
X[:,3] = A**(-1.0/3.0)
X[:,4] = A**(-1.0)
# Then nice printout using pandas
DesignMatrix = pd.DataFrame(X)
DesignMatrix.index = A
DesignMatrix.columns = ['1', 'A', 'A^(2/3)', 'A^(-1/3)', '1/A']
display(DesignMatrix)| 1 | A | A^(2/3) | A^(-1/3) | 1/A | |
|---|---|---|---|---|---|
| A | |||||
| 1 | 1.0 | 1.0 | 1.000000 | 1.000000 | 1.000000 |
| 2 | 1.0 | 2.0 | 1.587401 | 0.793701 | 0.500000 |
| 3 | 1.0 | 3.0 | 2.080084 | 0.693361 | 0.333333 |
| 4 | 1.0 | 4.0 | 2.519842 | 0.629961 | 0.250000 |
| 5 | 1.0 | 5.0 | 2.924018 | 0.584804 | 0.200000 |
| ... | ... | ... | ... | ... | ... |
| 264 | 1.0 | 264.0 | 41.153106 | 0.155883 | 0.003788 |
| 265 | 1.0 | 265.0 | 41.256962 | 0.155687 | 0.003774 |
| 266 | 1.0 | 266.0 | 41.360688 | 0.155491 | 0.003759 |
| 269 | 1.0 | 269.0 | 41.671089 | 0.154911 | 0.003717 |
| 270 | 1.0 | 270.0 | 41.774300 | 0.154720 | 0.003704 |
267 rows × 5 columns
In [2]:
# matrix inversion to find beta
beta = np.linalg.inv(X.T.dot(X)).dot(X.T).dot(Energies)
# and then make the prediction
ytilde = X @ betaIn [3]:
fit = np.linalg.lstsq(X, Energies, rcond =None)[0]
ytildenp = np.dot(fit,X.T)In [4]:
Masses['Eapprox'] = ytilde
# Generate a plot comparing the experimental with the fitted values values.
fig, ax = plt.subplots()
ax.set_xlabel(r'$A = N + Z$')
ax.set_ylabel(r'$E_\mathrm{bind}\,/\mathrm{MeV}$')
ax.plot(Masses['A'], Masses['Ebinding'], alpha=0.7, lw=2,
label='Ame2016')
ax.plot(Masses['A'], Masses['Eapprox'], alpha=0.7, lw=2, c='m',
label='Fit')
ax.legend()
save_fig("Masses2016OLS")
plt.show()In [5]:
def R2(y_data, y_model):
return 1 - np.sum((y_data - y_model) ** 2) / np.sum((y_data - np.mean(y_data)) ** 2)In [6]:
print(R2(Energies,ytilde))0.9547578478889096
In [7]:
def MSE(y_data,y_model):
n = np.size(y_model)
return np.sum((y_data-y_model)**2)/n
print(MSE(Energies,ytilde))0.037875961483052376
In [8]:
def RelativeError(y_data,y_model):
return abs((y_data-y_model)/y_data)
print(RelativeError(Energies, ytilde))A
1 0 inf
2 1 1.123190
3 2 0.327631
4 6 0.344172
5 9 0.044402
...
264 3304 0.009911
265 3310 0.009154
266 3317 0.007824
269 3338 0.011347
270 3344 0.009790
Name: Ebinding, Length: 267, dtype: float64
Warning:
Output truncated. This notebook contains too many cells to display efficiently.