Accessing the contents of a stanfit object

Stan Development Team

2016-12-18

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)
Warning: There were 3 divergent transitions after warmup. Increasing adapt_delta above 0.8 may help. See
http://mc-stan.org/misc/warnings.html#divergent-transitions-after-warmup
Warning: Examine the pairs() plot to diagnose sampling problems
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.

list_of_draws <- extract(fit)
print(names(list_of_draws))
[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:

head(list_of_draws$mu)
[1] 17.485041  7.811984 17.485555  7.892279 10.900572  2.891970
head(list_of_draws$tau)
[1] 1.0471755 0.4587495 9.9680219 0.1007184 3.2761209 4.2399999
head(list_of_draws$theta)
          
iterations      [,1]       [,2]      [,3]      [,4]      [,5]      [,6]
      [1,] 19.280406 17.7994321 18.617346 17.110101 17.355373 16.274623
      [2,]  8.615382  7.7502768  8.300409  7.874559  7.684811  7.671890
      [3,] 24.357232 22.3714257 -5.547075 15.853349  8.016347 15.191077
      [4,]  7.845430  7.8820044  7.771158  7.904784  8.067953  7.920660
      [5,]  9.602114 12.1133064 12.560551 15.147464  7.692133  9.891669
      [6,]  0.627199  0.3938821  6.913463 -1.260281 -2.926099  1.562390
          
iterations      [,7]      [,8]
      [1,] 16.912059 17.078409
      [2,]  7.059112  8.121503
      [3,] 27.031001 34.452037
      [4,]  7.891332  7.797977
      [5,] 12.454904  9.428657
      [6,]  6.996572  1.247808


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:

matrix_of_draws <- as.matrix(fit)
print(colnames(matrix_of_draws))
 [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__"    
df_of_draws <- as.data.frame(fit)
print(colnames(df_of_draws))
 [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__"    
array_of_draws <- as.array(fit)
print(dimnames(array_of_draws))
$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:

print(dim(matrix_of_draws))
print(dim(df_of_draws))
print(dim(array_of_draws))
[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:

mu_and_theta1 <- as.matrix(fit, pars = c("mu", "theta[1]"))
head(mu_and_theta1)
          parameters
iterations       mu  theta[1]
      [1,] 9.918092 29.056100
      [2,] 8.219386  9.988808
      [3,] 5.749259  8.174150
      [4,] 5.715203  7.101814
      [5,] 4.331191  5.383148
      [6,] 6.377430  5.144341


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.73097198 0.11747698 4.8519300  -1.5476445   4.4913829
tau        6.22545709 0.14666104 5.3577321   0.2280811   2.1846471
eta[1]     0.39765414 0.01776585 0.8952167  -1.4952902  -0.1830388
eta[2]     0.02151760 0.01652189 0.8276465  -1.6944626  -0.4817205
eta[3]    -0.17819643 0.01621554 0.9018889  -1.9399048  -0.7864082
eta[4]    -0.02292258 0.01764078 0.8902046  -1.8254567  -0.5965126
eta[5]    -0.33512060 0.01860331 0.8962075  -2.1252321  -0.9099636
eta[6]    -0.18254378 0.01761093 0.9248454  -1.9414058  -0.7983549
eta[7]     0.32848149 0.01764591 0.8882398  -1.4300651  -0.2462207
eta[8]     0.08280693 0.01643426 0.9214570  -1.7289300  -0.5404707
theta[1]  11.02768652 0.18006530 7.8209783  -1.0917102   5.9453471
theta[2]   7.82521271 0.10157504 5.8712819  -3.8140961   4.0899849
theta[3]   6.24526633 0.13681235 7.2558193  -9.9829204   2.3216931
theta[4]   7.61239346 0.10639108 6.3846174  -5.9922316   3.9095378
theta[5]   5.13645659 0.10889147 6.3431920  -8.3392041   1.3608763
theta[6]   6.05291473 0.11653215 6.6570064  -8.0640270   2.0994204
theta[7]  10.29722247 0.13646111 6.7005241  -1.1360283   5.6585567
theta[8]   8.38318070 0.15316388 7.7916731  -6.5958234   3.7543853
lp__     -39.52491534 0.07979577 2.6765786 -45.4407483 -41.1778574
                   50%         75%      97.5%    n_eff      Rhat
mu         7.710300640  10.8201595  17.911860 1705.782 1.0011798
tau        4.934660076   8.6865976  20.060263 1334.543 1.0002570
eta[1]     0.446463362   0.9974604   2.123625 2539.127 1.0015390
eta[2]     0.032663748   0.5577853   1.660854 2509.403 1.0003477
eta[3]    -0.176944010   0.4221599   1.568372 3093.450 1.0001283
eta[4]    -0.009576566   0.5522899   1.707047 2546.503 1.0016068
eta[5]    -0.332853459   0.2384485   1.511246 2320.794 1.0005783
eta[6]    -0.200192814   0.3973778   1.721141 2757.869 1.0006981
eta[7]     0.336165749   0.9267875   2.107704 2533.799 1.0005539
eta[8]     0.089375860   0.7237699   1.879359 3143.764 0.9993661
theta[1]   9.987920033  14.8592466  30.175273 1886.523 0.9995775
theta[2]   7.776935129  11.5481649  19.714621 3341.118 0.9995209
theta[3]   6.694730289  10.6788537  19.591093 2812.693 1.0007810
theta[4]   7.619696315  11.4786594  20.276933 3601.300 1.0003961
theta[5]   5.569884496   9.4783695  16.218592 3393.345 1.0005970
theta[6]   6.214564570  10.3633545  18.442690 3263.371 1.0000984
theta[7]   9.654788879  14.1503354  25.270990 2411.014 0.9999078
theta[8]   8.094676929  12.6035994  25.343160 2587.907 1.0011317
lp__     -39.275838629 -37.6116771 -34.939284 1125.124 1.0001105

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.730972 0.117477 4.851930 1.5780088 14.02927 1705.782 1.001180
tau 6.225457 0.146661 5.357732 0.8157504 13.64416 1334.543 1.000257

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.5780088 14.02927
tau 0.8157504 13.64416


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.8600566 0.8313094 0.8103682 0.7940533
max_treedepth_by_chain <- sapply(sampler_params, function(x) max(x[, "treedepth__"]))
print(max_treedepth_by_chain)
[1] 4 4 4 3


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.116136

$tau
[1] 0.4804578

$eta
[1]  1.434757  1.950301  1.948562 -1.830643 -1.710742 -1.614347  1.036691
[8] -1.598933

$theta
[1] -0.4267955 -0.1790988 -0.1799339 -1.9956827 -1.9380753 -1.8917616
[7] -0.6180498 -1.8843557


(P)RNG seed

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

print(get_seed(fit))
[1] 1244074710


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.029773 0.029291
chain:2 0.025882 0.025523
chain:3 0.027275 0.030373
chain:4 0.026627 0.022287