library(ggplot2)
library(mrgsolve)
library(minqa)
library(dplyr)
library(magrittr)
options(mrgsolve.soloc="build")
This tutorial illustrates how to do MAP Bayes estimation with
mrgsolve
.
The setup was adapted from an existing project, where only a single
individual was considered. With some additional R
coding,
it could be expanded to consider multiple individuals in a single
run.
This document shows how to simulate some data and then re-estimate the MAP Bayes estimates. For clarity, just the optimization piece has been included in a separate doc map_bayes_example.html.
The model specification code below is for a one-compartment
model, where mrgsolve
will calculate the amount in
CENT
from closed-form equations
For now, $OMEGA
and $SIGMA
are filled
with zeros; we’ll update it later
The control stream is set up so that we can either simulate the
etas or pass them in. ETA(1)
and ETA(2)
are
the etas that mrgsolve
will draw from $OMEGA
.
ETA1
and ETA2
are fixed and known at the time
of time of the simulation. The optimizer will search for values of
ETA1
and ETA2
that optimize the objective
function. Note that ETA1
and ETA2
must be in
the parameter list for this to work
We do a trick where CL=TVCL*exp(ETA1+ETA(1));
The
assumption is that either ETA1
(simulating) is zero or
ETA(1)
is zero (estimating)
We table out ETA(1)
and ETA(2)
so we
can know the true (simulated) values (but not both zero and not both
non-zero)
DV
is output as a function of EPS(1)
;
this will be zero until we add in values for $SIGMA
. But
when we’re estimating, we need to make sure that EPS(1)
is
zero; the prediction shouldn’t have any randomness in it (just the
individual prediction based on known etas)
<- '
code $SET request=""
$PARAM TVCL=1.5, TVVC=23.4, ETA1=0, ETA2=0
$CMT CENT
$PKMODEL ncmt=1
$OMEGA 0 0
$SIGMA 0
$MAIN
double CL = TVCL*exp(ETA1 + ETA(1));
double V = TVVC*exp(ETA2 + ETA(2));
$TABLE
capture DV = (CENT/V)*(1+EPS(1));
capture ET1 = ETA(1);
capture ET2 = ETA(2);
'
<- mcode_cache("map", code) mod
$OMEGA
and $SIGMA
;
cmat
call makes a 2x2
matrix where the
off-diagonal is a correlation (?cmat
).<- cmat(0.23,-0.78, 0.62)
omega <- solve(omega)
omega.inv <- matrix(0.0032) sigma
Just a single dose to CENT
with an events object
<- ev(amt=750,cmt=1) dose
Take these times for concentration observations
<- c(0.5,12,24) sampl
Simulate
$OMEGA
and $SIGMA
so that the simulated data will be randomcarry.out
all of the items that we
will need in the estimation data set (doses, evid, etc)end=-1
with add=sampl
makes sure
that we only get observation records at the times listed in
sampl
set.seed(1012)
<-
sim %>%
mod ev(dose) %>%
omat(omega) %>%
smat(sigma) %>%
carry_out(amt,evid,cmt) %>%
mrgsim(end=-1, add=sampl)
sim
. Model: map
. Dim: 4 x 8
. Time: 0 to 24
. ID: 1
. ID time evid amt cmt DV ET1 ET2
. 1: 1 0.0 1 750 1 41.067 0.5196 -0.2728
. 2: 1 0.5 0 0 0 42.749 0.5196 -0.2728
. 3: 1 12.0 0 0 0 6.932 0.5196 -0.2728
. 4: 1 24.0 0 0 0 1.375 0.5196 -0.2728
DV
to NA
for the dosing record<- mutate(sim, DV = ifelse(evid==1, NA_real_, DV)) sim
Create a data set to use in the optimization
ET1
and ET2
since they are
in the parameter list<- sim %>% select(-ET1, -ET2)
data
data
. # A tibble: 4 × 6
. ID time evid amt cmt DV
. <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
. 1 1 0 1 750 1 NA
. 2 1 0.5 0 0 0 42.7
. 3 1 12 0 0 0 6.93
. 4 1 24 0 0 0 1.37
This function takes in a set of proposed \(\eta\)s along with the observed data vector, the data set and a model object and returns the value of the EBE objective function
When we do the estimation, the fixed effects and random effect variances are fixed.
The estimates are the \(\eta\) for clearance and volume
Arguments:
eta
the current values from the optimizerycol
the observed data column named
the data setm
the model objectdvcol
the predicted data column namepred
if TRUE
, just return predicted
valueseta
is a listeta
is properly named (i.e. ETA1
and ETA2
)eta
into a matrix that is one rowm
) with the current values of
ETA1
and ETA2
d
and save output to
out
objectif(pred)
) return
the simulated data<- function(eta,d,ycol,m,dvcol,pred=FALSE) {
mapbayes
<- as.numeric(sigma)
sig2 <- as.list(eta)
eta names(eta) <- names(init)
<- eta %>% unlist %>% matrix(nrow=1)
eta_m <- param(m,eta)
m <- m %>% zero_re() %>% mrgsim(data=d,output="df")
out if(pred) return(out)
# http://www.ncbi.nlm.nih.gov/pmc/articles/PMC3339294/
<- out[[dvcol]]^2*sig2
sig2j <- log(sig2j) + (1/sig2j)*(d[[ycol]] - out[[dvcol]])^2
sqwres <- diag(eta_m %*% omega.inv %*% t(eta_m))
nOn return(sum(sqwres,na.rm=TRUE) + nOn)
}
<- c(ETA1=-0.3, ETA2=0.2) init
Fit the data
newuoa
is from the minqa
packageoptim
) could probably also be
usedArguments to newuoa
mapbayes
<- nloptr::newuoa(init,mapbayes,ycol="DV",m=mod,d=data,dvcol="DV") fit
Here are the final estimates
$par fit
. [1] 0.4995400 -0.3274858
Here are the simulated values
slice(sim,1) %>% select(ET1, ET2)
. # A tibble: 1 × 2
. ET1 ET2
. <dbl> <dbl>
. 1 0.520 -0.273
A data set and model to get predictions; this will give us a smooth prediction line
<- data %>% filter(evid==1)
pdata <- mod %>% update(end=24, delta=0.1) pmod
Predicted line based on final estimates
<- mapbayes(fit$par,ycol="DV",pdata,pmod,dvcol="DV",pred=TRUE) %>% filter(time > 0)
pred head(pred)
. ID time DV ET1 ET2
. 1 1 0.1 43.82331 0 0
. 2 1 0.2 43.18567 0 0
. 3 1 0.3 42.55731 0 0
. 4 1 0.4 41.93809 0 0
. 5 1 0.5 41.32789 0 0
. 6 1 0.6 40.72656 0 0
Predicted line based on initial estimates
<- mapbayes(init,ycol="DV",pdata,pmod,dvcol="DV",pred=TRUE) %>% filter(time > 0)
initial head(initial)
. ID time DV ET1 ET2
. 1 1 0.1 26.13954 0 0
. 2 1 0.2 26.03811 0 0
. 3 1 0.3 25.93707 0 0
. 4 1 0.4 25.83642 0 0
. 5 1 0.5 25.73616 0 0
. 6 1 0.6 25.63629 0 0
Plot
ggplot() +
geom_line(data=pred, aes(time,DV),col="firebrick", lwd=1) +
geom_line(data=initial,aes(time,DV), lty=2, col="darkgreen", lwd=1) +
geom_point(data=data %>% filter(evid==0), aes(time,DV), col="darkslateblue",size=3) +
theme_bw()
mrgsolve: mrgsolve.github.io | metrum research group: metrumrg.com