Postprocessing MCMC draws

Each MCMC run generates posterior draws (samples) in .ext and .iph, as described in output files. In this article we show how to postprocess these draws in R.

Consider the following example. The model uses ITS to position the initial draws, followed by NUTS sampler that draws.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
$PROB RUN# Example 1 (from samp5l)
$INPUT C SET ID JID TIME  DV=CONC AMT=DOSE RATE EVID MDV CMT
$DATA w15.csv IGNORE=@

$SUBROUTINES ADVAN3 TRANS4 ;# Two compartment Model

$PK
MU_1=THETA(1)
MU_2=THETA(2)
MU_3=THETA(3)
MU_4=THETA(4)
;# ETAs: normally distruted with mean 0, variance OMEGA().
;# PHI=MU+ETA: normally distruted with mean MU, and variance OMEGA()
;# thus individual parameters CL,V1,Q,V2 follow log-normal distribution.
CL=DEXP(MU_1+ETA(1))
V1=DEXP(MU_2+ETA(2))
Q=DEXP(MU_3+ETA(3))
V2=DEXP(MU_4+ETA(4))
S1=V1

;# Y is normally distributed, with sd is F*SQRT(SIGMA(1,1))
$ERROR
Y = F + F*EPS(1)

;# Initial values of THETA, OMEGA, SIGMA
$THETA
 2.0 ;# [LN(CL)]
 2.0 ;# [LN(V1)]
 2.0 ;# [LN(Q)]
 2.0 ;# [LN(V2)]
$OMEGA BLOCK(4)
0.15 ;# [P]
0.01 ;# [F]
0.15 ;# [P]
0.01 ;# [F]
0.01 ;# [F]
0.15 ;# [P]
0.01 ;# [F]
0.01 ;# [F]
0.01 ;# [F]
0.15 ;# [P]
$SIGMA
(0.6 )   ;# [P]

$PRIOR NWPRI
;# THETA Priors follow normal distribution (use $TTDF for t-distriution).
;# This normal prior is specfied by $THETAP (mean) and $THETAPV (covariance)
$THETAP (2.0 FIX) (2.0 FIX) (2.0 FIX) (2.0 FIX)
 ;# uninformative prior with large variance
$THETAPV BLOCK(4) FIXED VALUES(50.0,0.0)

;# Inverse Wishart dist for OMEGA's prior, unless $OLKJDF, $OVARF used.
$OMEGAP BLOCK(4) FIXED VALUES(0.15,0.0)
;# Uninformative prior: low degrees of freedom, equivalent to block dimension.
$OMEGAPD (4 FIX)

;# Inverse Wishart dist for SIGMA's prior, unless $SLKJDF, $SVARF used
$SIGMAP (0.06 FIXED)
$SIGMAPD (1 FIXED)

$EST METHOD=ITS INTERACTION NITER=0 NOABORT NOPRIOR=1 file=w15_its.ext
$EST METHOD=NUTS AUTO=1 PRINT=100 NITER=1000 NOPRIOR=0 BAYES_PHI_STORE=1 file=w15.ext delim=q

The raw output file contains the draws for THETA, OMEGA, and SIGMA:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
TABLE NO.     2: NUTS Bayesian Analysis: Goal Function=AVERAGE VALUE OF LIKELIHOOD FUNCTION: Problem=1 Subproblem=0 Superproblem1=0 Iteration1=0 Superproblem2=0 Iteration2=0
ITERATION,THETA1,THETA2,THETA3,THETA4,"SIGMA(1,1)","OMEGA(1,1)","OMEGA(2,1)","OMEGA(2,2)","OMEGA(3,1)","OMEGA(3,2)","OMEGA(3,3)","OMEGA(4,1)","OMEGA(4,2)","OMEGA(4,3)","OMEGA(4,4)",MCMCOBJ
-300,2.00000E+00,2.00000E+00,2.00000E+00,2.00000E+00,6.00000E-01,1.50000E-01,1.51376E-02,1.50000E-01,1.51376E-02,1.55683E-02,1.50000E-01,1.51376E-02,1.55683E-02,1.59466E-02,1.50000E-01,-1481.0595036333802
-299,2.00000E+00,2.00000E+00,2.00000E+00,2.00000E+00,6.00000E-01,1.50000E-01,1.51376E-02,1.50000E-01,1.51376E-02,1.55683E-02,1.50000E-01,1.51376E-02,1.55683E-02,1.59466E-02,1.50000E-01,-1481.0595036333802
-298,1.77021E+00,1.92367E+00,1.67629E+00,2.41448E+00,1.89903E-01,5.94126E-02,-1.70499E-02,7.59678E-02,1.35122E-02,1.50691E-02,7.14325E-01,-2.41652E-02,-3.42371E-02,-1.26046E+00,3.05555E+00,-1690.4727243536261
-297,1.68026E+00,1.67408E+00,1.35288E+00,2.59168E+00,2.52116E-01,5.95871E-02,1.55883E-02,6.95303E-02,-5.63175E-02,1.28377E-02,1.56139E-01,1.98344E-01,9.76716E-02,-2.36819E-01,1.10078E+00,-1940.4909307481009
-296,1.79628E+00,1.74109E+00,1.27945E+00,2.75333E+00,1.97092E-01,6.00569E-02,1.75012E-02,2.75713E-02,-5.99059E-02,-2.12877E-02,1.53612E-01,8.24030E-02,2.54620E-02,-1.12590E-01,2.75134E-01,-2088.5224564870337
...
-2,1.57703E+00,1.51858E+00,7.84907E-01,2.30962E+00,7.09675E-02,4.49183E-02,3.44580E-02,6.43152E-02,-4.41176E-02,-6.58071E-02,1.61325E-01,6.26977E-02,6.90618E-02,-1.33745E-01,2.44828E-01,-2624.3454338693418
-1,1.64192E+00,1.55751E+00,7.77636E-01,2.36215E+00,6.19459E-02,3.54253E-02,1.67610E-02,5.47438E-02,-3.43736E-02,-1.66851E-02,1.09793E-01,5.19755E-02,1.31541E-02,-1.03461E-01,2.15894E-01,-2652.9377915999407
0,1.61352E+00,1.52061E+00,7.33446E-01,2.39472E+00,6.09613E-02,5.88703E-02,4.86180E-02,7.75862E-02,-2.60756E-02,-1.72516E-02,1.37898E-01,1.02105E-01,7.88645E-02,-1.38651E-01,3.47987E-01,-2662.8617338419945
1,1.59717E+00,1.50404E+00,7.97282E-01,2.36552E+00,5.71047E-02,5.61861E-02,4.92907E-02,8.73577E-02,-3.54973E-02,-2.78835E-02,1.47221E-01,9.46009E-02,8.54477E-02,-1.12673E-01,2.79091E-01,-2621.7603074785320
2,1.62582E+00,1.57701E+00,7.09708E-01,2.50196E+00,5.26333E-02,5.29024E-02,5.08796E-02,9.89421E-02,-3.32450E-02,-3.71433E-02,1.07969E-01,9.24031E-02,9.13087E-02,-1.14224E-01,3.31564E-01,-2637.4017870707512
3,1.59873E+00,1.50936E+00,7.83598E-01,2.32496E+00,6.54131E-02,4.74182E-02,4.24135E-02,9.71055E-02,-1.85747E-02,-4.70037E-03,7.91404E-02,7.85960E-02,3.40789E-02,-4.43496E-02,3.32503E-01,-2574.7507661689292
4,1.63602E+00,1.56174E+00,7.87609E-01,2.43435E+00,5.93954E-02,4.78977E-02,3.87123E-02,7.99926E-02,-3.64555E-02,-3.34183E-02,1.29314E-01,7.05832E-02,3.46432E-02,-9.52120E-02,2.45398E-01,-2603.3891973207692
5,1.59396E+00,1.47458E+00,7.97147E-01,2.39761E+00,5.28784E-02,5.75504E-02,5.38026E-02,1.03129E-01,-4.10376E-02,-3.57055E-02,1.00605E-01,9.48757E-02,9.68799E-02,-1.03949E-01,3.44539E-01,-2656.8976786215057
...
998,1.60110E+00,1.55626E+00,6.87090E-01,2.30176E+00,5.42115E-02,6.41064E-02,4.80244E-02,6.21091E-02,-1.08583E-02,-5.12719E-04,5.12472E-02,1.11934E-01,7.43562E-02,-5.16238E-02,3.27015E-01,-2748.2552208643006
999,1.60503E+00,1.57259E+00,6.37331E-01,2.30317E+00,5.56863E-02,6.38367E-02,4.74873E-02,6.49443E-02,-1.29483E-02,-3.02370E-03,5.57051E-02,1.10373E-01,7.64161E-02,-5.92531E-02,3.09326E-01,-2750.6036449963908
1000,1.61987E+00,1.63438E+00,6.67028E-01,2.27359E+00,5.96975E-02,6.80918E-02,4.98584E-02,7.97527E-02,-2.08863E-02,2.55142E-03,8.15478E-02,1.31526E-01,9.59568E-02,-9.01345E-02,4.17237E-01,-2711.0823149117732
-1000000000,1.62095E+00,1.57352E+00,7.41249E-01,2.37879E+00,5.71851E-02,5.34615E-02,4.13122E-02,7.71840E-02,-2.95543E-02,-1.54367E-02,1.35301E-01,8.48112E-02,6.20176E-02,-1.01418E-01,2.84249E-01,-2669.6279018999371
-1000000001,2.65270E-02,3.79117E-02,5.69494E-02,6.14311E-02,5.68017E-03,9.69483E-03,1.16750E-02,1.95409E-02,1.39471E-02,1.97815E-02,3.88705E-02,1.99779E-02,2.47545E-02,3.12851E-02,5.54416E-02,46.913591858677314
-1000000004,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,2.38845E-01,2.30274E-01,6.38565E-01,2.75637E-01,-3.54991E-01,-1.57795E-01,3.64134E-01,6.84519E-01,4.16392E-01,-5.29926E-01,5.30689E-01,0.0000000000000000
-1000000005,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,1.17631E-02,2.08789E-02,8.76315E-02,3.47749E-02,1.53212E-01,1.91446E-01,5.20560E-02,6.89126E-02,1.29489E-01,1.45714E-01,5.11943E-02,0.0000000000000000
-1000000006,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.0000000000000000
-1000000007,2.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.0000000000000000

Note that the delim=q option for $EST METHOD=NUTS effectively saves .ext draws as a csv file and adds quotation to the column names. The NITER=1000 option requests 1000 draws. Their summary and diagnostic information is printed on lines with dummy iteration id's "-100000000x".

NONMEM provides a utility routine "format_draws.f90" to format the the draw files for future csv parsing. So by building and running the standalone tool on the .ext file

1
2
3
4
5
  # compiles the fortran utility
  gfortran format_draws.f90 -o format_draws

  # save the output as csv
  ./format_draws w15.ext w15.ext.csv

one obtains a new w15.ext.csv file with the original draw but title and summary lines commented out:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# TABLE NO.     2: NUTS Bayesian Analysis: Goal Function=AVERAGE VALUE OF LIKELIHOOD FUNCTION: Problem=1 Subproblem=0 Superproblem1=0 Iteration1=0 Superproblem2=0 Iteration2=0
ITERATION,THETA1,THETA2,THETA3,THETA4,"SIGMA(1,1)","OMEGA(1,1)","OMEGA(2,1)","OMEGA(2,2)","OMEGA(3,1)","OMEGA(3,2)","OMEGA(3,3)","OMEGA(4,1)","OMEGA(4,2)","OMEGA(4,3)","OMEGA(4,4)",MCMCOBJ
-300,2.00000E+00,2.00000E+00,2.00000E+00,2.00000E+00,6.00000E-01,1.50000E-01,1.51376E-02,1.50000E-01,1.51376E-02,1.55683E-02,1.50000E-01,1.51376E-02,1.55683E-02,1.59466E-02,1.50000E-01,-1481.0595036333802
-299,2.00000E+00,2.00000E+00,2.00000E+00,2.00000E+00,6.00000E-01,1.50000E-01,1.51376E-02,1.50000E-01,1.51376E-02,1.55683E-02,1.50000E-01,1.51376E-02,1.55683E-02,1.59466E-02,1.50000E-01,-1481.0595036333802
-298,1.77021E+00,1.92367E+00,1.67629E+00,2.41448E+00,1.89903E-01,5.94126E-02,-1.70499E-02,7.59678E-02,1.35122E-02,1.50691E-02,7.14325E-01,-2.41652E-02,-3.42371E-02,-1.26046E+00,3.05555E+00,-1690.4727243536261
...
999,1.60503E+00,1.57259E+00,6.37331E-01,2.30317E+00,5.56863E-02,6.38367E-02,4.74873E-02,6.49443E-02,-1.29483E-02,-3.02370E-03,5.57051E-02,1.10373E-01,7.64161E-02,-5.92531E-02,3.09326E-01,-2750.6036449963908
1000,1.61987E+00,1.63438E+00,6.67028E-01,2.27359E+00,5.96975E-02,6.80918E-02,4.98584E-02,7.97527E-02,-2.08863E-02,2.55142E-03,8.15478E-02,1.31526E-01,9.59568E-02,-9.01345E-02,4.17237E-01,-2711.0823149117732
# -1000000000,1.62095E+00,1.57352E+00,7.41249E-01,2.37879E+00,5.71851E-02,5.34615E-02,4.13122E-02,7.71840E-02,-2.95543E-02,-1.54367E-02,1.35301E-01,8.48112E-02,6.20176E-02,-1.01418E-01,2.84249E-01,-2669.6279018999371
# -1000000001,2.65270E-02,3.79117E-02,5.69494E-02,6.14311E-02,5.68017E-03,9.69483E-03,1.16750E-02,1.95409E-02,1.39471E-02,1.97815E-02,3.88705E-02,1.99779E-02,2.47545E-02,3.12851E-02,5.54416E-02,46.913591858677314
# -1000000004,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,2.38845E-01,2.30274E-01,6.38565E-01,2.75637E-01,-3.54991E-01,-1.57795E-01,3.64134E-01,6.84519E-01,4.16392E-01,-5.29926E-01,5.30689E-01,0.0000000000000000
# -1000000005,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,1.17631E-02,2.08789E-02,8.76315E-02,3.47749E-02,1.53212E-01,1.91446E-01,5.20560E-02,6.89126E-02,1.29489E-01,1.45714E-01,5.11943E-02,0.0000000000000000
# -1000000006,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.0000000000000000
# -1000000007,2.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.0000000000000000

Then we can read this file in R:

1
  data <- read.csv("w15.ext.csv", comment.char="#")

Alternatively, one can read the original .ext file and skip the title and summary lines:

1
  data <- read.csv(text = head(readLines("w15.ext"), -6), skip=1)

Note that the "-6" argument here is ad hoc, in that the raw output files may contain a different number of summary and diagnostic lines. Alternatively one can simply remove the lines with ITERATION<=-1000000000.

Now we can do postprocessing through the R package posterior. We first read in the data object as draws. We also skip the warmup (burn-in) draws, the ones with ITERATION < 0 (the code shows ligatures so it may not be the same as how it is typed, e.g. the R pipe "|>" is shown as triangle |>).

1
2
3
  library(posterior)
  d <- as_draws(data[data$ITERATION>=0, ])
  d$ITERATION <- NULL

We first examine the summary of the draws.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
  summary(d)
  #> # A tibble: 16 Ă— 10
  #>    variable         mean     median       sd      mad         q5         q95  rhat ess_bulk ess_tail
  #>    <chr>           <dbl>      <dbl>    <dbl>    <dbl>      <dbl>       <dbl> <dbl>    <dbl>    <dbl>
  #>  1 THETA1         1.62       1.62    0.0265   0.0265      1.58       1.66    0.999     740.     824.
  #>  2 THETA2         1.57       1.57    0.0379   0.0393      1.51       1.63    1.00      488.     643.
  #>  3 THETA3         0.741      0.740   0.0569   0.0567      0.646      0.831   1.000     334.     475.
  #>  4 THETA4         2.38       2.38    0.0614   0.0598      2.27       2.48    1.00      482.     723.
  #>  5 SIGMA.1.1.     0.0572     0.0567  0.00568  0.00539     0.0485     0.0668  0.999     380.     403.
  #>  6 OMEGA.1.1.     0.0535     0.0527  0.00969  0.00957     0.0386     0.0705  1.000     541.     753.
  #>  7 OMEGA.2.1.     0.0413     0.0406  0.0117   0.0122      0.0244     0.0621  1.01      272.     510.
  #>  8 OMEGA.2.2.     0.0772     0.0749  0.0195   0.0187      0.0482     0.113   1.01      245.     382.
  #>  9 OMEGA.3.1.    -0.0296    -0.0295  0.0139   0.0130     -0.0522    -0.00624 1.000     292.     438.
  #> 10 OMEGA.3.2.    -0.0154    -0.0163  0.0198   0.0186     -0.0477     0.0185  1.00      214.     390.
  #> 11 OMEGA.3.3.     0.135      0.130   0.0389   0.0367      0.0810     0.206   1.00      223.     345.
  #> 12 OMEGA.4.1.     0.0848     0.0830  0.0200   0.0195      0.0557     0.120   1.000     486.     763.
  #> 13 OMEGA.4.2.     0.0620     0.0613  0.0248   0.0252      0.0241     0.103   1.00      311.     518.
  #> 14 OMEGA.4.3.    -0.101     -0.102   0.0313   0.0298     -0.155     -0.0541  1.000     263.     347.
  #> 15 OMEGA.4.4.     0.284      0.278   0.0554   0.0543      0.208      0.379   1.000     350.     755.
  #> 16 MCMCOBJ    -2670.     -2669.     46.9     45.3     -2750.     -2596.      1.01      200.     276.

Note that the file read maps index notation, e.g. "SIGMA(1,1)" to "SIGMA.1.1." and "PHI(1)" to "PHI.1.". The standard deviation in the summary is exactly what is reported on dummy ITERATION "-1000000001" in the original raw output. Details of rhat, ess_bulk, and ess_tail can be found here.

We can visulize the draws using the bayesplot package. For example,

1
2
3
  library(bayesplot)

  mcmc_dens(d)

plots the posterior desity,

/docs/examples/nuts-post/dens.png

and

1
  mcmc_pairs(d, regex_pars="THETA*")

visulizes the correlation between THETAs (regex_pars option selects the parameters by regular expression, here all the THETA variables). We can see that the clearance and the volume of distribution V1 & V2 are weakly correlated.

/docs/examples/nuts-post/theta_pairs.png

With option BAYES_PHI_STORE=1, the individual draws are also output in the .iph file. We can follow the same steps to read in those draws. Note that .iph file does not contain the summary lines.

1
2
data_ind <- read.csv("w15.iph", skip=1)
d_ind <- as_draws(data_ind[data_ind$ITERATION>=0, ])

This draw object is much bigger than the population parameters, as it contains the draws of PHIs and ETAs for each of the 100 subjects.

The posterior package provides many functions to manipulate draws. For example, we can extract the individual parameters for subject 1, and bind_draws with the population parameter draws.

1
2
3
  d1 <- bind_draws(d_ind |> dplyr::filter(SUBJECT_NO==1) |> dplyr::select(-"MCMCOBJ"),
                   d |> dplyr::select(-"MCMCOBJ"),
                   along='variable')

The following pair plot shows that PHI and ETA are indeeded correlated, and the residual error is likely not associated with them.

1
d1 |> mcmc_pairs(pars=c("PHI.1.","ETA.1.","SIGMA.1.1."))
/docs/examples/nuts-post/phi1_sigma_pairs.png

We encourage users to explore the mentioned R packages for additional features.