Neutropenia (PKPD)
example-pkpd-neutropenia.Rmd
This examples shows how to implement the Friberg neutropenia model. What is implemented here is a generic version of it, i.e. not for a specific drug. We will sample from the posterior for the individual patients using TDM data and neutrophil count data.
We will first create the Stan model and sample from the posterior.
First, we define the model and parameters. Note that in this exmaple
we specify ruv
as a list that contains separate residual
errors for pk
- and pd
-labeled
observations.
parameters <- list(CL = 5, V = 50,
SLOPE = 0.1, MTT = 100,
CIRC0 = 5, GAMMA = 0.2)
iiv <- list(
CL = 0.2, V = 0.5,
SLOPE = 1.0, MTT = 0.2,
CIRC0 = 0.5, GAMMA = 0.2
)
ruv <- list(
pk = list(add = 0.2),
pd = list(add = 0.3)
)
parameter_definitions <- list(
"CL" = "CL",
"Q" = 0,
"V" = "V",
"V2" = 1,
"KA" = 0,
"MTT" = "MTT",
"CIRC0" = "CIRC0",
"GAMMA" = "GAMMA",
"ALPHA" = "SLOPE"
)
ode <- "
real ka = 0;
real k10 = CL / V;
real k12 = 0;
real k21 = 0;
real ktr = 4 / MTT;
real conc;
real EDrug;
real transit1;
real transit2;
real transit3;
real circ;
real prol;
dAdt[1] = -KA * A[1];
dAdt[2] = KA * A[1] - (k10 + k12) * A[2] + k21 * A[3];
dAdt[3] = k12 * A[2] - k21 * A[3];
conc = A[2] / V;
EDrug = ALPHA * conc; // slope model, not Emax
prol = A[4] + CIRC0;
transit1 = A[5] + CIRC0;
transit2 = A[6] + CIRC0;
transit3 = A[7] + CIRC0;
circ = fmax(machine_precision(), A[8] + CIRC0); // Device for implementing a modeled
// initial condition
dAdt[4] = ktr * prol * ((1 - EDrug) * ((CIRC0 / circ)^GAMMA) - 1);
dAdt[5] = ktr * (prol - transit1);
dAdt[6] = ktr * (transit1 - transit2);
dAdt[7] = ktr * (transit2 - transit3);
dAdt[8] = ktr * (transit3 - circ);
"
model <- new_stan_model(
parameters = parameters,
parameter_definitions = parameter_definitions,
ode = ode,
covariate_definitions = NULL,
solver = 'pmx_solve_rk45',
obs_types = c("pk", "pd"),
custom_ipred = list(
"pk" = "A[2, ] ./ V;",
"pd" = "A[8, ] + theta[7];"
),
verbose = T
)
model_file <- write_stan_model(model)
and compile the model:
mod <- load_model(
model_file
)
Define the input data and prepare for use in Stan. Note that in the
definition of tdm_data
we use the type
argument to distinguish between PK and PD observations.
regimen <- new_regimen(
amt = 1500,
n = 3,
times = c(0, 24, 48),
type = 'infusion',
cmt = 2,
t_inf = 2
)
covariates <- NULL
tdm_data <- data.frame(
t = c(2.5, 11.5),
dv = c(40, 14),
type = "pk"
)
pd_data <- data.frame(
t = c(3, 6, 9, 12) * 24,
dv = c(5, 1.5, .8, 2),
type = "pd"
)
comb_data <- rbind(
tdm_data,
pd_data
)
data <- new_stan_data(
regimen,
covariates = covariates,
data = comb_data,
parameters = parameters,
iiv = iiv,
ltbs = list(pk = TRUE, pd = TRUE),
ruv = ruv,
dose_cmt = 2
)
Then, we can sample from the posterior using:
post <- get_mcmc_posterior(
mod = mod,
data = data
)
## Running MCMC with 1 chain...
##
## Chain 1 Iteration: 1 / 1000 [ 0%] (Warmup)
## Chain 1 Iteration: 50 / 1000 [ 5%] (Warmup)
## Chain 1 Iteration: 100 / 1000 [ 10%] (Warmup)
## Chain 1 Iteration: 150 / 1000 [ 15%] (Warmup)
## Chain 1 Iteration: 200 / 1000 [ 20%] (Warmup)
## Chain 1 Iteration: 250 / 1000 [ 25%] (Warmup)
## Chain 1 Iteration: 300 / 1000 [ 30%] (Warmup)
## Chain 1 Iteration: 350 / 1000 [ 35%] (Warmup)
## Chain 1 Iteration: 400 / 1000 [ 40%] (Warmup)
## Chain 1 Iteration: 450 / 1000 [ 45%] (Warmup)
## Chain 1 Iteration: 500 / 1000 [ 50%] (Warmup)
## Chain 1 Iteration: 501 / 1000 [ 50%] (Sampling)
## Chain 1 Iteration: 550 / 1000 [ 55%] (Sampling)
## Chain 1 Iteration: 600 / 1000 [ 60%] (Sampling)
## Chain 1 Iteration: 650 / 1000 [ 65%] (Sampling)
## Chain 1 Iteration: 700 / 1000 [ 70%] (Sampling)
## Chain 1 Iteration: 750 / 1000 [ 75%] (Sampling)
## Chain 1 Iteration: 800 / 1000 [ 80%] (Sampling)
## Chain 1 Iteration: 850 / 1000 [ 85%] (Sampling)
## Chain 1 Iteration: 900 / 1000 [ 90%] (Sampling)
## Chain 1 Iteration: 950 / 1000 [ 95%] (Sampling)
## Chain 1 Iteration: 1000 / 1000 [100%] (Sampling)
## Chain 1 finished in 57.2 seconds.
Summary info about the posterior:
post
## Parameters:
## posterior_mode mean median sd q5 q95 rhat
## CL 4.069 4.220 4.201 0.539 3.355 5.132 1.006
## V 34.584 36.997 36.002 7.330 26.283 50.079 1.001
## SLOPE 0.060 0.059 0.058 0.016 0.035 0.086 0.998
## MTT 94.742 95.011 94.716 8.627 81.397 109.523 0.999
## CIRC0 5.014 5.686 5.464 1.440 3.742 8.090 1.003
## GAMMA 0.186 0.204 0.197 0.041 0.148 0.279 1.004
##
## Observed data, posterior:
## type time dv mean loc pct pct5 pct95
## 1 pk 2.5 40.0 35.1969640 -----|---o- 0.808 26.0733850 45.252310
## 2 pk 11.5 14.0 12.1645054 -----|---o- 0.838 9.3491765 15.305140
## 3 pd 72.0 5.0 4.5255091 -----|--o-- 0.708 3.1641995 6.190220
## 4 pd 144.0 1.5 1.7014853 --o--|----- 0.278 1.1376500 2.271029
## 5 pd 216.0 0.8 0.8655025 ---o-|----- 0.436 0.5374418 1.301487
## 6 pd 288.0 2.0 2.0045579 -----|-o--- 0.554 1.2407715 3.024030
Plot parameter distributions:
plot_params(post)