Imports¶
Install packages¶
!sudo /bin/bash -c "(source /venv/bin/activate; pip install --quiet jupyterlab-vim)"
!jupyter labextension enable!sudo /bin/bash -c "(source /venv/bin/activate; pip install --quiet graphviz)"!sudo /bin/bash -c "(source /venv/bin/activate; pip install --quiet dataframe_image)"Import modules¶
%load_ext autoreload
%autoreload 2
import arviz as az
import pymc as pm
import numpy as np
import scipy.stats as stats
import matplotlib.pyplot as pltWARNING (pytensor.tensor.blas): Using NumPy C-API based implementation for BLAS functions.
dir_name = "./L07_data"
!ls $dir_nameimport msml610.tutorials.msml610_utils as ut
ut.config_notebook()# Setting notebook style
# Notebook signature
Python 3.12.3
Linux ef783958e0e8 6.10.14-linuxkit #1 SMP Tue Apr 15 16:00:54 UTC 2025 aarch64 aarch64 aarch64 GNU/Linux
numpy version=1.26.4
pymc version=5.18.2
matplotlib version=3.10.3
arviz version=0.21.0
preliz version=0.19.0
Chemical Shift¶
Gaussian inference¶
data = np.loadtxt(f"{dir_name}/chemical_shifts.csv")
print(len(data), data)
# print(sorted(data))
# It looks Gaussian with a couple of outliers.
az.plot_kde(data, rug=True)
title = "Chemical shift"
ut.process_figure(title)48 [51.06 55.12 53.73 50.24 52.05 56.4 48.45 52.34 55.65 51.49 51.86 63.43
53. 56.09 51.93 52.31 52.33 57.48 57.44 55.14 53.93 54.62 56.09 68.58
51.36 55.47 50.73 51.94 54.95 50.39 52.91 51.5 52.68 47.72 49.73 51.82
54.99 52.84 53.19 54.52 51.46 53.73 51.61 49.81 52.42 54.3 53.84 53.16]

with pm.Model() as model_g:
# The mean is Uniform in [40, 70] (which is larger than the data).
mu = pm.Uniform("mu", lower=40, upper=70)
# The std dev is half normal with a large value (which is a large value based on the data).
sigma = pm.HalfNormal("sigma", sigma=10)
# The model is N(mu, sigma).
y = pm.Normal("y", mu=mu, sigma=sigma, observed=data)
# Sample.
idata_g = pm.sample(1000)Auto-assigning NUTS sampler...
Initializing NUTS using jitter+adapt_diag...
Multiprocess sampling (4 chains in 4 jobs)
NUTS: [mu, sigma]
Loading...
Loading...
Sampling 4 chains for 1_000 tune and 1_000 draw iterations (4_000 + 4_000 draws total) took 1 seconds.
pm.model_to_graphviz(model_g)Loading...
# There are 4 traces for 2 variables.
az.plot_trace(idata_g)
# The posterior distribution of the params is bi-dimensional, since it has mu and sigma.
az.plot_pair(idata_g, kind="kde", marginals=True)
# Report a summary of the inference.
az.summary(idata_g, kind="stats").round(2)Loading...
# Compute 100 posterior predictive samples.
y_pred_g = pm.sample_posterior_predictive(idata_g, model=model_g)Sampling: [y]
Loading...
Loading...
# Black: KDE of the data (observed)
# Blue: KDEs of the posterior predictive samples
# Orange: KDE of the posterior predictive mean
az.plot_ppc(y_pred_g, mean=True, num_pp_samples=100)
Student-t¶
# Show the PDF for various values of \nu.
# Points to be used to sample the PDF.
x_values = np.linspace(-10, 10, 500)
# Plot t-student sweeping \nu.
for df in [0.1, 0.5, 1, 2, 5, 10, 30]:
# Student-t with df.
distr = stats.t(df)
# Compute PDF.
x_pdf = distr.pdf(x_values)
plt.plot(x_values, x_pdf, label=f"nu={df}")
# Plot gaussian.
x_pdf = stats.norm.pdf(x_values)
plt.plot(x_values, x_pdf, "k--", label="Gauss / nu=infty")
plt.xlim(-5, 5)
plt.legend()
title = "Chap7: Student-t"
ut.process_figure(title)
# Use a Student-T model.
with pm.Model() as model_t:
mu = pm.Uniform("mu", 40, 75)
sigma = pm.HalfNormal("sigma", sigma=10)
# A student with nu = 30 is close to a Gaussian.
nu = pm.Exponential("nu", 1 / 30)
#
y = pm.StudentT("y", mu=mu, sigma=sigma, nu=nu, observed=data)
idata_t = pm.sample(1_000)Auto-assigning NUTS sampler...
Initializing NUTS using jitter+adapt_diag...
Multiprocess sampling (4 chains in 4 jobs)
NUTS: [mu, sigma, nu]
Loading...
Loading...
Sampling 4 chains for 1_000 tune and 1_000 draw iterations (4_000 + 4_000 draws total) took 2 seconds.
az.plot_trace(idata_t)
az.summary(idata_t, kind="stats").round(2)Loading...
# Compute 100 posterior predictive samples.
y_ppc_t = pm.sample_posterior_predictive(idata_t, model_t)Sampling: [y]
Loading...
Loading...
ax = az.plot_ppc(y_ppc_t, num_pp_samples=100, mean=True)
ax.set_xlim(40, 70)