Accessing the contents of a stanfit object

Stan Development Team

2020-07-27

This vignette demonstrates how to access most of data stored in a stanfit object. A stanfit object (an object of class "stanfit") contains the output derived from fitting a Stan model using Markov chain Monte Carlo or one of Stan’s variational approximations (meanfield or full-rank). Throughout the document we’ll use the stanfit object obtained from fitting the Eight Schools example model:

library(rstan)
fit <- stan_demo("eight_schools", refresh = 0)
class(fit)
[1] "stanfit"
attr(,"package")
[1] "rstan"

Posterior draws

There are several functions that can be used to access the draws from the posterior distribution stored in a stanfit object. These are extract, as.matrix, as.data.frame, and as.array, each of which returns the draws in a different format.


extract()

The extract function (with its default arguments) returns a list with named components corresponding to the model parameters.

[1] "mu"    "tau"   "eta"   "theta" "lp__" 

In this model the parameters mu and tau are scalars and theta is a vector with eight elements. This means that the draws for mu and tau will be vectors (with length equal to the number of post-warmup iterations times the number of chains) and the draws for theta will be a matrix, with each column corresponding to one of the eight components:

[1] 13.755611  5.088973 14.567504 10.966277  9.329064  9.746773
[1]  5.584752  1.425946 15.275256  1.151014  5.980760  5.171067
          
iterations      [,1]      [,2]      [,3]      [,4]      [,5]      [,6]
      [1,] 11.257421 12.968000 16.426172 13.004674  2.068346  7.825838
      [2,]  6.939401  6.645859  4.544993  5.314296  3.955333  6.546783
      [3,]  4.316781 15.154667 19.726759 14.355710  7.619603 11.677133
      [4,] 10.070333 11.544674  9.669666  8.450212 10.693776 10.058468
      [5,]  6.163263 11.234223  7.725507  9.205691  7.601304  8.187862
      [6,] 12.823124  6.502476 16.686937 19.391841 14.496071 17.015973
          
iterations      [,7]      [,8]
      [1,] 14.361420 12.331327
      [2,]  4.433757  5.960468
      [3,] 17.586321 14.896491
      [4,]  9.333538 11.061507
      [5,]  6.424973  5.623696
      [6,]  8.722546 12.142126


as.matrix(), as.data.frame(), as.array()

The as.matrix, as.data.frame, and as.array functions can also be used to retrieve the posterior draws from a stanfit object:

 [1] "mu"       "tau"      "eta[1]"   "eta[2]"   "eta[3]"   "eta[4]"  
 [7] "eta[5]"   "eta[6]"   "eta[7]"   "eta[8]"   "theta[1]" "theta[2]"
[13] "theta[3]" "theta[4]" "theta[5]" "theta[6]" "theta[7]" "theta[8]"
[19] "lp__"    
 [1] "mu"       "tau"      "eta[1]"   "eta[2]"   "eta[3]"   "eta[4]"  
 [7] "eta[5]"   "eta[6]"   "eta[7]"   "eta[8]"   "theta[1]" "theta[2]"
[13] "theta[3]" "theta[4]" "theta[5]" "theta[6]" "theta[7]" "theta[8]"
[19] "lp__"    
$iterations
NULL

$chains
[1] "chain:1" "chain:2" "chain:3" "chain:4"

$parameters
 [1] "mu"       "tau"      "eta[1]"   "eta[2]"   "eta[3]"   "eta[4]"  
 [7] "eta[5]"   "eta[6]"   "eta[7]"   "eta[8]"   "theta[1]" "theta[2]"
[13] "theta[3]" "theta[4]" "theta[5]" "theta[6]" "theta[7]" "theta[8]"
[19] "lp__"    

The as.matrix and as.data.frame methods essentially return the same thing except in matrix and data frame form, respectively. The as.array method returns the draws from each chain separately and so has an additional dimension:

[1] 4000   19
[1] 4000   19
[1] 1000    4   19

By default all of the functions for retrieving the posterior draws return the draws for all parameters (and generated quantities). The optional argument pars (a character vector) can be used if only a subset of the parameters is desired, for example:

          parameters
iterations         mu  theta[1]
      [1,]  8.9870595  7.620988
      [2,]  0.7078515  7.170478
      [3,] 13.6528703 13.054554
      [4,]  6.2690719  6.867772
      [5,]  7.3994697  6.221234
      [6,]  2.8647763 12.793815


Posterior summary statistics and convergence diagnostics

Summary statistics are obtained using the summary function. The object returned is a list with two components:

fit_summary <- summary(fit)
print(names(fit_summary))
[1] "summary"   "c_summary"

In fit_summary$summary all chains are merged whereas fit_summary$c_summary contains summaries for each chain individually. Typically we want the summary for all chains merged, which is what we’ll focus on here.

The summary is a matrix with rows corresponding to parameters and columns to the various summary quantities. These include the posterior mean, the posterior standard deviation, and various quantiles computed from the draws. The probs argument can be used to specify which quantiles to compute and pars can be used to specify a subset of parameters to include in the summary.

For models fit using MCMC, also included in the summary are the Monte Carlo standard error (se_mean), the effective sample size (n_eff), and the R-hat statistic (Rhat).

print(fit_summary$summary)
                  mean    se_mean        sd        2.5%         25%
mu         7.949724852 0.11645748 5.0220423  -1.8312424   4.7576375
tau        6.767792859 0.26071994 6.3574731   0.2470565   2.5041545
eta[1]     0.408701730 0.01386516 0.9501978  -1.4874739  -0.2086203
eta[2]     0.007727134 0.01322405 0.8849320  -1.7512963  -0.5762528
eta[3]    -0.192114553 0.01404407 0.9226422  -2.0030901  -0.7969951
eta[4]    -0.033759894 0.01318734 0.8612730  -1.7409539  -0.5971957
eta[5]    -0.346857560 0.01364171 0.8636999  -1.9529636  -0.9314834
eta[6]    -0.230036510 0.01384811 0.8815712  -1.9260533  -0.7873415
eta[7]     0.343687116 0.01377346 0.8887923  -1.4161332  -0.2242292
eta[8]     0.035561913 0.01366197 0.9363172  -1.8079512  -0.5818113
theta[1]  11.462536053 0.15084188 8.3682751  -2.3660092   5.9851278
theta[2]   7.918125583 0.09708623 6.5484705  -5.1976169   3.8269726
theta[3]   6.180546976 0.12959986 7.8215647 -11.7517178   2.0698119
theta[4]   7.619393810 0.09174090 6.3401521  -4.7389570   3.7137076
theta[5]   5.105055688 0.10064363 6.2743073  -8.5951843   1.4489822
theta[6]   5.845512902 0.12290817 6.9074684  -9.5681116   2.1296288
theta[7]  10.599010949 0.11468754 6.8046805  -1.3860025   6.1034108
theta[8]   8.283245665 0.13464191 8.1685831  -8.0754557   3.6377522
lp__     -39.526738826 0.07853786 2.6176025 -45.3577084 -41.0664787
                   50%         75%      97.5%     n_eff      Rhat
mu         7.835402041  11.1348996  18.358286 1859.6276 1.0005667
tau        5.335645186   9.3223878  20.538802  594.5941 1.0047787
eta[1]     0.421892594   1.0377818   2.231475 4696.5429 0.9997337
eta[2]     0.009115021   0.5805529   1.706422 4478.0686 0.9997160
eta[3]    -0.207774375   0.4224644   1.641023 4315.9954 1.0009866
eta[4]    -0.031837545   0.5329859   1.670423 4265.4744 0.9991571
eta[5]    -0.371184839   0.1986612   1.444355 4008.5562 0.9995339
eta[6]    -0.240246536   0.3500333   1.538437 4052.6007 0.9999744
eta[7]     0.355068745   0.9188961   2.098127 4164.0347 0.9994926
eta[8]     0.039034679   0.6629433   1.848352 4696.9896 1.0006744
theta[1]  10.274798188  15.7183875  30.977015 3077.7122 1.0003125
theta[2]   7.704921763  11.9399689  21.363006 4549.5085 1.0002197
theta[3]   6.720866758  10.8636365  21.017014 3642.3212 0.9997713
theta[4]   7.510177300  11.4861718  20.160995 4776.0998 0.9994167
theta[5]   5.670108610   9.2081225  16.259682 3886.5025 0.9991307
theta[6]   6.241934340  10.2265114  18.556742 3158.4669 1.0006471
theta[7]   9.874164480  14.5963734  25.669570 3520.3269 0.9999671
theta[8]   8.047692191  12.9014021  26.050090 3680.7215 1.0003636
lp__     -39.276377470 -37.7010733 -35.102266 1110.8341 1.0025522

If, for example, we wanted the only quantiles included to be 10% and 90%, and for only the parameters included to be mu and tau, we would specify that like this:

mu_tau_summary <- summary(fit, pars = c("mu", "tau"), probs = c(0.1, 0.9))$summary
print(mu_tau_summary)
        mean   se_mean       sd      10%      90%     n_eff     Rhat
mu  7.949725 0.1164575 5.022042 1.728870 14.23924 1859.6276 1.000567
tau 6.767793 0.2607199 6.357473 1.045481 13.80119  594.5941 1.004779

Since mu_tau_summary is a matrix we can pull out columns using their names:

mu_tau_80pct <- mu_tau_summary[, c("10%", "90%")]
print(mu_tau_80pct)
         10%      90%
mu  1.728870 14.23924
tau 1.045481 13.80119


Sampler diagnostics

For models fit using MCMC the stanfit object will also contain the values of parameters used for the sampler. The get_sampler_params function can be used to access this information.

The object returned by get_sampler_params is a list with one component (a matrix) per chain. Each of the matrices has number of columns corresponding to the number of sampler parameters and the column names provide the parameter names. The optional argument inc_warmup (defaulting to TRUE) indicates whether to include the warmup period.

sampler_params <- get_sampler_params(fit, inc_warmup = FALSE)
sampler_params_chain1 <- sampler_params[[1]]
colnames(sampler_params_chain1)
[1] "accept_stat__" "stepsize__"    "treedepth__"   "n_leapfrog__" 
[5] "divergent__"   "energy__"     

To do things like calculate the average value of accept_stat__ for each chain (or the maximum value of treedepth__ for each chain if using the NUTS algorithm, etc.) the sapply function is useful as it will apply the same function to each component of sampler_params:

mean_accept_stat_by_chain <- sapply(sampler_params, function(x) mean(x[, "accept_stat__"]))
print(mean_accept_stat_by_chain)
[1] 0.8601258 0.8803538 0.8626561 0.9327039
max_treedepth_by_chain <- sapply(sampler_params, function(x) max(x[, "treedepth__"]))
print(max_treedepth_by_chain)
[1] 4 4 5 5


Model code

The Stan program itself is also stored in the stanfit object and can be accessed using get_stancode:

code <- get_stancode(fit)

The object code is a single string and is not very intelligible when printed:

print(code)
[1] "data {\n  int<lower=0> J;          // number of schools \n  real y[J];               // estimated treatment effects\n  real<lower=0> sigma[J];  // s.e. of effect estimates \n}\nparameters {\n  real mu; \n  real<lower=0> tau;\n  vector[J] eta;\n}\ntransformed parameters {\n  vector[J] theta;\n  theta = mu + tau * eta;\n}\nmodel {\n  target += normal_lpdf(eta | 0, 1);\n  target += normal_lpdf(y | theta, sigma);\n}"
attr(,"model_name2")
[1] "schools"

A readable version can be printed using cat:

cat(code)
data {
  int<lower=0> J;          // number of schools 
  real y[J];               // estimated treatment effects
  real<lower=0> sigma[J];  // s.e. of effect estimates 
}
parameters {
  real mu; 
  real<lower=0> tau;
  vector[J] eta;
}
transformed parameters {
  vector[J] theta;
  theta = mu + tau * eta;
}
model {
  target += normal_lpdf(eta | 0, 1);
  target += normal_lpdf(y | theta, sigma);
}


Initial values

The get_inits function returns initial values as a list with one component per chain. Each component is itself a (named) list containing the initial values for each parameter for the corresponding chain:

inits <- get_inits(fit)
inits_chain1 <- inits[[1]]
print(inits_chain1)
$mu
[1] -1.001074

$tau
[1] 0.3153299

$eta
[1] -0.002760239  1.212609451 -0.538926504 -0.604310311 -1.852417385
[6]  0.064908690 -0.803602925  1.006946375

$theta
[1] -1.0019444 -0.6187019 -1.1710136 -1.1916311 -1.5851966 -0.9806063 -1.2544740
[8] -0.6835537


(P)RNG seed

The get_seed function returns the (P)RNG seed as an integer:

print(get_seed(fit))
[1] 1007283841


Warmup and sampling times

The get_elapsed_time function returns a matrix with the warmup and sampling times for each chain:

print(get_elapsed_time(fit))
          warmup   sample
chain:1 0.031855 0.031802
chain:2 0.034966 0.031189
chain:3 0.033241 0.032088
chain:4 0.031447 0.036188