By the end of this section, you’ll understand that the Stan ecosystem is a stack of related projects mainly developed under stan-dev, ranging from the compiler and math library to CmdStan, language interfaces, and workflow packages. You’ll be able to place a typical modeling task within that stack and identify which layer each piece belongs to.
2.1 A minimal Stan workflow
We start our workflow by writing a minimal Stan model in a .stan file.
Additionally, we provide some data for the input and save it as a JSON file.
R · data
library(cmdstanr)stan_data <-list(N =20, y =rnorm(20, mean =5, sd =2))cmdstanr::write_stan_json(stan_data, "data.json")> stan_data$N[1] 20$y [1] 4.5435894.8997504.0816208.6986144.8270032.9038138.6596155.165155 [9] 5.1481271.2066752.3685447.8739815.0712585.3223906.9134317.336083[17] 3.4622793.7173101.6201413.577615
2.1.1 Language interfaces
Next we can compile and sample from the Stan model. To do this, we use an interface, such as cmdstanr for R or cmdstanpy for Python, that wraps CmdStan.
CmdStan invokes
stanc3 to transpile the Stan model into a C++ file, which is then compiled and linked against
the math library (implementing built-in functions for distributions, linear algebra, ODE solvers, etc., along with automatic differentiation) and
the stan library (implementing the inference algorithms, such as HMC/NUTS).
The result is a standalone executable that the interface then runs to produce posterior samples.
Compile and sample from the Stan model in R with cmdstanr. Use the fitted model to extract posterior draws, plot MCMC trace plots with bayesplot, and run approximate leave-one-out cross-validation with loo.
R · cmdstanr
library(cmdstanr)library(bayesplot)library(loo)# compile Stan modelmod <- cmdstanr::cmdstan_model("model.stan")# sample from the modelfit <- mod$sample(data ="data.json", chains =4, iter_sampling =1000)# get drawsdraws <- fit$draws(variables =c("mu", "sigma"))# plotting and cross-validationbayesplot::mcmc_trace(draws)loo::loo(fit$draws("log_lik"))
Compile and sample from the Stan model in Python with cmdstanpy. Use the fitted model to extract posterior draws, plot MCMC trace plots, and run approximate leave-one-out cross-validation using arviz.
Python · cmdstanpy
from cmdstanpy import CmdStanModelimport arviz as az# compile Stan modelmod = CmdStanModel(stan_file="model.stan")# sample from the modelfit = mod.sample(data="data.json", chains=4, iter_sampling=1000)# get drawsidata = az.from_cmdstanpy(fit, log_likelihood="log_lik")# plotting and cross-validationaz.plot_trace(idata, var_names=["mu", "sigma"])az.loo(idata)
cmdstanr and cmdstanpy are thin language wrappers around CmdStan, the command-line interface to Stan. CmdStan itself invokes stanc3 to compile a .stan file into a standalone executable, which can be run directly from the command line to sample from (or optimize, etc.) the model.
Bash
# run from the CmdStan directorymake model./model sample num_chains=4 num_samples=1000 \ data file=data.json \ output file=output.csv$CMDSTAN/bin/stansummary output_*.csv$CMDSTAN/bin/diagnose output_*.csv
2.1.2 Higher-level modelling frameworks
Instead of writing a Stan model from scratch, you can use higher-level modelling frameworks that let you specify your model using formula syntax. Two example frameworks are
brms, which dynamically generates and compiles custom Stan code for your specified model, and
rstanarm, which fits your model using a set of pre-compiled Stan programs for common model families (avoiding compilation time, at the cost of some flexibility).
R · brms
library(brms)dat <-data.frame(y = jsonlite::fromJSON("data.json")$y)fit <- brms::brm(formula = y ~1,data = dat,family =gaussian(),prior =c(prior(normal(0, 10), class = Intercept),prior(exponential(1), class = sigma) ),chains =4,iter =2000)