Step 4 - Convergence Diagnostics
Notation
Before trusting any of the posterior draws from Step 3, check that the chains actually converged and mixed well:
- R-hat ($\hat{R}$): compares within-chain and between-chain variance for each parameter. Values close to 1.00 (conventionally < 1.01) indicate the chains converged to the same distribution; larger values mean at least one chain hasn't mixed properly and the draws shouldn't be trusted yet.
- Effective sample size (ESS): the number of independent samples the correlated MCMC draws are actually worth. Low ESS relative to the number of draws indicates high autocorrelation within a chain.
- Trace plots: a visual check — each chain's parameter value over the sampling iterations should look like unstructured noise (a "hairy caterpillar"), with all chains overlapping the same range rather than drifting apart.
step 1 - summary table (R-hat, ESS)
import subprocess
%run ./bayesian/step4_diagnostics.py
%run ./dataframes/truncate_with_ellipsis.py
df = diagnostics_summary(trace)
subprocess.run(['wl-copy'], input=truncate_with_ellipsis(df, 3).to_html().encode())
| mean | sd | hdi_3% | ... | ess_bulk | ess_tail | r_hat | |
|---|---|---|---|---|---|---|---|
| mu_PC1 | 0.011 | 0.552 | -1.052 | ... | 11203.0 | 6692.0 | 1.0 |
| log_sigma_PC1 | 1.671 | 0.073 | 1.537 | ... | 9556.0 | 6230.0 | 1.0 |
| mu_PC2 | -0.003 | 0.151 | -0.284 | ... | 9794.0 | 6197.0 | 1.0 |
| ... | ... | ... | ... | ... | ... | ... | ... |
| sigma_PC1 | 5.333 | 0.391 | 4.652 | ... | 9556.0 | 6230.0 | 1.0 |
| sigma_PC2 | 1.463 | 0.11 | 1.268 | ... | 11278.0 | 6386.0 | 1.0 |
| sigma_PC3 | 0.517 | 0.038 | 0.45 | ... | 11880.0 | 6863.0 | 1.0 |
Interpretation
Convergence
Convergence looks excellent across the board: $\hat{R}=1.0$ for every parameter (all 4 chains agree, no stuck or diverging chains), and ESS bulk/tail in the thousands against 8,000 total draws (4 chains × 2,000) — ESS this close to, or exceeding, the raw draw count indicates NUTS sampled this posterior very efficiently. Not surprising given the model: three independent, low-dimensional, well-identified Normal-likelihood groups is about as easy a target as MCMC gets.
Cross-check against Classical PCA
A useful cross-check: the posterior means for $\sigma_i$ line up almost exactly with $\sqrt{\text{eigenvalue}i}$ from Classical PCA's Step 8 — $\sigma{PC1}=5.333$ vs $\sqrt{27.95}=5.29$, $\sigma_{PC2}=1.463$ vs $\sqrt{2.11}=1.45$, $\sigma_{PC3}=0.517$ vs $\sqrt{0.262}=0.51$. This is exactly the outcome aim 3 was looking for: with an uninformative prior, the Bayesian posterior recovers essentially the same numbers as classical PCA, confirming the model and sampler are correctly wired up ahead of Step 7's more formal comparison.
step 2 - trace plots
%run ./bayesian/step4_diagnostics.py
trace_plot_chart(trace, "output/snippets_png/bayesian_step4_trace_plot.png")

Python
filename: step4_diagnostics.py
import arviz as az
import matplotlib.pyplot as plt
'''
convergence diagnostics for the step 3 posterior trace: a summary table of R-hat and
effective sample size per parameter, and trace plots for a visual check.
'''
def diagnostics_summary(trace):
return az.summary(trace)
def trace_plot_chart(trace, filename):
az.plot_trace(trace)
plt.savefig(filename)
return