Author: Stan Development Team
Date: 2020-07-27

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

    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] 13.755611  5.088973 14.567504 10.966277  9.329064  9.746773

    head(list_of_draws$tau)

    [1]  5.584752  1.425946 15.275256  1.151014  5.980760  5.171067

    head(list_of_draws$theta)

    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:

    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,]  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