Skill Tracer
Skills › Scientific Agent Skills › pymc › standard_workflow.md

standard_workflow.md

96 lines · skills/pymc/references/standard_workflow.md at 5f2bc03

Open on GitHub
1# Standard Bayesian Workflow
2
3This self-contained example was executed with PyMC 6.3.2, PyTensor 3.3.2 and
4ArviZ 1.3.0. It tests the API and changing prediction row counts with small
5synthetic data. Two short chains cannot validate a scientific inference.
6For a real analysis, stop after the prior predictive plot to inspect it, and
7choose sampling effort using numerical diagnostics and the estimand's MCSE.
8
9```python
10import arviz as az
11import numpy as np
12import pymc as pm
14rng = np.random.default_rng(7)
15x = np.linspace(-1, 1, 30)
16y = 0.5 + 1.2 * x + rng.normal(0, 0.3, len(x))
17with pm.Model(coords={"obs_id": np.arange(len(y))}) as model:
18 x_data = pm.Data("x", x, dims="obs_id")
19 alpha = pm.Normal("alpha", 0, sigma=1)
20 beta = pm.Normal("beta", 0, sigma=1)
21 sigma = pm.HalfNormal("sigma", sigma=1)
22 mu = pm.Deterministic("mu", alpha + beta * x_data, dims="obs_id")
23 pm.Normal("y_obs", mu, sigma=sigma, observed=y,
24 shape=x_data.shape[0], dims="obs_id")
25 prior = pm.sample_prior_predictive(draws=50, random_seed=7)
27prior_plot = az.plot_ppc_dist(prior, group="prior_predictive", num_samples=20)
28with model:
29 idata = pm.sample(draws=80, tune=80, chains=2, cores=1,
30 nuts_sampler="pymc", target_accept=.9,
31 random_seed=7, progressbar=False)
32 pm.compute_log_likelihood(idata, progressbar=False)
33 pm.sample_posterior_predictive(idata, extend_inferencedata=True,
34 random_seed=8, progressbar=False)
36summary = az.summary(idata, var_names=["alpha", "beta", "sigma"], round_to="none")
37print(summary)
38posterior_plot = az.plot_ppc_dist(idata, num_samples=20)
39assert idata.log_likelihood["y_obs"].shape == (2, 80, 30)
41x_new = np.array([-0.5, 0., 0.5, 1.5])
42with model:
43 pm.set_data({"x": x_new}, coords={"obs_id": np.arange(len(x_new))})
44 predictions = pm.sample_posterior_predictive(
45 idata, var_names=["mu", "y_obs"], predictions=True,
46 random_seed=9, progressbar=False)
47 pm.set_data({"x": x}, coords={"obs_id": np.arange(len(x))})
49assert predictions.predictions["y_obs"].shape == (2, 80, 4)
50mean = predictions.predictions["y_obs"].mean(("chain", "draw"))
51interval = az.hdi(predictions.predictions["y_obs"], prob=.95)
52print(mean, interval.sel(ci_bound="lower"), interval.sel(ci_bound="upper"))
53```
55For saved figures use `prior_plot.savefig("prior.png")` and the equivalent for
56the posterior plot. For Matplotlib, close the actual figure with
57`plt.close(prior_plot.viz["figure"].item())` after importing `matplotlib.pyplot`.
58The runnable templates save/close plots and serialize posterior and prediction
59trees separately.

Standard Bayesian Workflow

This self-contained example was executed with PyMC 6.3.2, PyTensor 3.3.2 and ArviZ 1.3.0. It tests the API and changing prediction row counts with small synthetic data. Two short chains cannot validate a scientific inference. For a real analysis, stop after the prior predictive plot to inspect it, and choose sampling effort using numerical diagnostics and the estimand's MCSE.

import arviz as az
import numpy as np
import pymc as pm

rng = np.random.default_rng(7)
x = np.linspace(-1, 1, 30)
y = 0.5 + 1.2 * x + rng.normal(0, 0.3, len(x))
with pm.Model(coords={"obs_id": np.arange(len(y))}) as model:
    x_data = pm.Data("x", x, dims="obs_id")
    alpha = pm.Normal("alpha", 0, sigma=1)
    beta = pm.Normal("beta", 0, sigma=1)
    sigma = pm.HalfNormal("sigma", sigma=1)
    mu = pm.Deterministic("mu", alpha + beta * x_data, dims="obs_id")
    pm.Normal("y_obs", mu, sigma=sigma, observed=y,
              shape=x_data.shape[0], dims="obs_id")
    prior = pm.sample_prior_predictive(draws=50, random_seed=7)

prior_plot = az.plot_ppc_dist(prior, group="prior_predictive", num_samples=20)
with model:
    idata = pm.sample(draws=80, tune=80, chains=2, cores=1,
                      nuts_sampler="pymc", target_accept=.9,
                      random_seed=7, progressbar=False)
    pm.compute_log_likelihood(idata, progressbar=False)
    pm.sample_posterior_predictive(idata, extend_inferencedata=True,
                                  random_seed=8, progressbar=False)

summary = az.summary(idata, var_names=["alpha", "beta", "sigma"], round_to="none")
print(summary)
posterior_plot = az.plot_ppc_dist(idata, num_samples=20)
assert idata.log_likelihood["y_obs"].shape == (2, 80, 30)

x_new = np.array([-0.5, 0., 0.5, 1.5])
with model:
    pm.set_data({"x": x_new}, coords={"obs_id": np.arange(len(x_new))})
    predictions = pm.sample_posterior_predictive(
        idata, var_names=["mu", "y_obs"], predictions=True,
        random_seed=9, progressbar=False)
    pm.set_data({"x": x}, coords={"obs_id": np.arange(len(x))})

assert predictions.predictions["y_obs"].shape == (2, 80, 4)
mean = predictions.predictions["y_obs"].mean(("chain", "draw"))
interval = az.hdi(predictions.predictions["y_obs"], prob=.95)
print(mean, interval.sel(ci_bound="lower"), interval.sel(ci_bound="upper"))

For saved figures use prior_plot.savefig("prior.png") and the equivalent for the posterior plot. For Matplotlib, close the actual figure with plt.close(prior_plot.viz["figure"].item()) after importing matplotlib.pyplot. The runnable templates save/close plots and serialize posterior and prediction trees separately.

Raw lines 1 to 60
61## Data and prediction contract
63`pm.Data` can change values and shape, but not array rank. With ordinary PyMC
64`dims`, labels document axes; they do not perform xarray-style symbolic alignment.
65Tie the likelihood's row count to the predictor shape, and supply replacement
66coordinates when a named dimension changes length. Update all data containers
67sharing that dimension in one `pm.set_data` call. `mutable=True`, `MutableData`,
68and `coords_mutable` are unnecessary legacy patterns for this target.
70Store out-of-sample results in the `predictions` group. `var_names` selects
71returned variables; it does not force posterior variables to be resampled.
72Deterministics depending on changed data are recomputed. `sample_vars` explicitly
73resamples named variables and `freeze_vars` deliberately reuses them; review
74warnings about changed data and posterior variables rather than suppressing them.
76`mu` represents uncertainty about the conditional mean; `y_obs` also includes
77observation noise. Neither automatically accounts for model misspecification,
78selection bias, or distribution shift at extrapolated predictors.

Data and prediction contract

pm.Data can change values and shape, but not array rank. With ordinary PyMC dims, labels document axes; they do not perform xarray-style symbolic alignment. Tie the likelihood's row count to the predictor shape, and supply replacement coordinates when a named dimension changes length. Update all data containers sharing that dimension in one pm.set_data call. mutable=True, MutableData, and coords_mutable are unnecessary legacy patterns for this target.

Store out-of-sample results in the predictions group. var_names selects returned variables; it does not force posterior variables to be resampled. Deterministics depending on changed data are recomputed. sample_vars explicitly resamples named variables and freeze_vars deliberately reuses them; review warnings about changed data and posterior variables rather than suppressing them.

mu represents uncertainty about the conditional mean; y_obs also includes observation noise. Neither automatically accounts for model misspecification, selection bias, or distribution shift at extrapolated predictors.

Raw lines 61 to 79
80## Diagnostics and scientific interpretation
82Use the bundled diagnostic helper with the selected parameters, inspect traces
83and joint posteriors, and evaluate MCSE for the actual scientific contrast. R-hat
84needs multiple independent chains. A NaN R-hat, absent sampler statistics or a
85constant parameter is not a pass. Low ESS calls for investigating geometry as
86well as increasing sampling effort. Divergences require explanation and repair;
87there is no generally acceptable nonzero quota.
89Prior predictive checks test implied assumptions; posterior predictive checks
90can reveal inadequacy of those assumptions. Neither proves parameter
91identifiability or generalization. Use prior sensitivity, simulated recovery and
92appropriate held-out validation where the scientific conclusion requires them.
94Sources: [Data](https://www.pymc.io/projects/docs/en/stable/api/generated/pymc.Data.html),
95[forward sampling](https://www.pymc.io/projects/docs/en/stable/_modules/pymc/sampling/forward.html),
96[log likelihood](https://www.pymc.io/projects/docs/en/stable/api/generated/pymc.stats.compute_log_likelihood.html).

Diagnostics and scientific interpretation

Use the bundled diagnostic helper with the selected parameters, inspect traces and joint posteriors, and evaluate MCSE for the actual scientific contrast. R-hat needs multiple independent chains. A NaN R-hat, absent sampler statistics or a constant parameter is not a pass. Low ESS calls for investigating geometry as well as increasing sampling effort. Divergences require explanation and repair; there is no generally acceptable nonzero quota.

Prior predictive checks test implied assumptions; posterior predictive checks can reveal inadequacy of those assumptions. Neither proves parameter identifiability or generalization. Use prior sensitivity, simulated recovery and appropriate held-out validation where the scientific conclusion requires them.

Sources: Data, forward sampling, log likelihood.

Raw lines 80 to 96