tidyvpc
and the Certara.RsNLME
package
integrate well together for a robust pharmacometric workflow. Build and
estimate your model using RsNLME and
easily pass the observed and simulated output data.frame
to
tidyvpc
for creating visual predictive checks (VPCs). The
below steps provide an example workflow when using the
tidyvpc
and Certara.RsNLME
package
together.
First, you must install the Certara.RsNLME
package.
Complete installation instructions can be found here.
Let’s take a quick look at the time-concentration profile of our
input dataset. We will be using the pkData
data.frame
from the Certara.RsNLME
package.
conc_data <- Certara.RsNLME::pkData
conc_data$Subject <- as.factor(conc_data$Subject)
ggplot(conc_data, aes(x = Act_Time, y = Conc, group = Subject, color = Subject)) +
scale_y_log10() +
geom_line() +
geom_point() +
ylab("Drug Concentration \n at the central compartment")
The above plot suggests that a two-compartment model with IV bolus is
a good starting point. Next, we will define the model using the
numCompartments
argument inside the function
pkmodel()
from the Certara.RsNLME
package, and
provide additional ‘column mapping’ arguments, which correspond to
required model variables mapped to column names in the
conc_data
data.frame
.
We will then pipe in additional functions to remove the random effect
from V2
, update initial estimates for fixed and random
effects, then change our error model.
model <- pkmodel(
numCompartments = 2,
data = conc_data,
ID = "Subject",
Time = "Act_Time",
A1 = "Amount",
CObs = "Conc",
modelName = "Two-Cmpt") %>%
structuralParameter(paramName = "V2", hasRandomEffect = FALSE) %>%
fixedEffect(effect = c("tvV", "tvCl", "tvV2", "tvCl2"),
value = c(15, 5, 40, 15)) %>%
randomEffect(effect = c("nV", "nCl", "nCl2"),
value = rep(0.1, 3)) %>%
residualError(predName = "C", SD = 0.2)
print(model)
#>
#> Model Overview
#> -------------------------------------------
#> Model Name : Two-Cmpt
#> Working Directory : C:/Users/jcraig/Documents/GitHub/tidyvpc/vignettes/Two-Cmpt
#> Is population : TRUE
#> Model Type : PK
#>
#> PK
#> -------------------------------------------
#> Parameterization : Clearance
#> Absorption : Intravenous
#> Num Compartments : 2
#> Dose Tlag? : FALSE
#> Elimination Comp ?: FALSE
#> Infusion Allowed ?: FALSE
#> Sequential : FALSE
#> Freeze PK : FALSE
#>
#> PML
#> -------------------------------------------
#> test(){
#> cfMicro(A1,Cl/V, Cl2/V, Cl2/V2)
#> dosepoint(A1)
#> C = A1 / V
#> error(CEps=0.2)
#> observe(CObs=C * ( 1 + CEps))
#> stparm(V = tvV * exp(nV))
#> stparm(Cl = tvCl * exp(nCl))
#> stparm(V2 = tvV2)
#> stparm(Cl2 = tvCl2 * exp(nCl2))
#> fixef( tvV = c(,15,))
#> fixef( tvCl = c(,5,))
#> fixef( tvV2 = c(,40,))
#> fixef( tvCl2 = c(,15,))
#> ranef(diag(nV,nCl,nCl2) = c(0.1,0.1,0.1))
#> }
#>
#> Structural Parameters
#> -------------------------------------------
#> V Cl V2 Cl2
#> -------------------------------------------
#> Observations:
#> Observation Name : CObs
#> Effect Name : C
#> Epsilon Name : CEps
#> Epsilon Type : Multiplicative
#> Epsilon frozen : FALSE
#> is BQL : FALSE
#> -------------------------------------------
#> Column Mappings
#> -------------------------------------------
#> Model Variable Name : Data Column name
#> id : Subject
#> time : Act_Time
#> A1 : Amount
#> CObs : Conc
Now that we have fit the model, we can create a new model with updated parameter estimates and perform our VPC simulation run.
vpc_model <- copyModel(model, acceptAllEffects = TRUE, modelName = "Two-Cmpt-VPC")
print(vpc_model)
#>
#> Model Overview
#> -------------------------------------------
#> Model Name : Two-Cmpt-VPC
#> Working Directory : C:/Users/jcraig/Documents/GitHub/tidyvpc/vignettes/Two-Cmpt-VPC
#> Is population : TRUE
#> Model Type : PK
#>
#> PK
#> -------------------------------------------
#> Parameterization : Clearance
#> Absorption : Intravenous
#> Num Compartments : 2
#> Dose Tlag? : FALSE
#> Elimination Comp ?: FALSE
#> Infusion Allowed ?: FALSE
#> Sequential : FALSE
#> Freeze PK : FALSE
#>
#> PML
#> -------------------------------------------
#> test(){
#> cfMicro(A1,Cl/V, Cl2/V, Cl2/V2)
#> dosepoint(A1)
#> C = A1 / V
#> error(CEps=0.161251376509829)
#> observe(CObs=C * ( 1 + CEps))
#> stparm(V = tvV * exp(nV))
#> stparm(Cl = tvCl * exp(nCl))
#> stparm(V2 = tvV2)
#> stparm(Cl2 = tvCl2 * exp(nCl2))
#> fixef( tvV = c(,15.3977961716836,))
#> fixef( tvCl = c(,6.61266919198735,))
#> fixef( tvV2 = c(,41.2018786759217,))
#> fixef( tvCl2 = c(,14.0301337530406,))
#> ranef(diag(nV,nCl,nCl2) = c(0.069404827604399,0.182196897237991,0.0427782148473702))
#>
#> }
#>
#> Structural Parameters
#> -------------------------------------------
#> V Cl V2 Cl2
#> -------------------------------------------
#> Observations:
#> Observation Name : CObs
#> Effect Name : C
#> Epsilon Name : CEps
#> Epsilon Type : Multiplicative
#> Epsilon frozen : FALSE
#> is BQL : FALSE
#> -------------------------------------------
#> Column Mappings
#> -------------------------------------------
#> Model Variable Name : Data Column name
#> id : Subject
#> time : Act_Time
#> A1 : Amount
#> CObs : Conc
Next, we will use the vpcmodel()
function to perform
simulation and generate the required observed and simulated input
data.frame
for tidyvpc
.
fit_vpc_sim <- vpcmodel(vpc_model)
The resulting observed and simulated data can be found in the
returned values from the vpcmodel()
function e.g.,
obs_data <- fit_vpc_sim$predcheck0
sim_data <- fit_vpc_sim$predout
The x
and y
arguments to
observed()
are the columns from
fit_vpc_sim$predcheck0
The x
and
y
arguments to simulated()
from
fit_vpc_sim$predout
. By default, x
column is
named IVAR
and y
column is named
DV
in the output tables generated from
vpcmodel()
.
# Create a binless VPC plot
binless_vpc <- observed(obs_data, x = IVAR, yobs = DV) %>%
simulated(sim_data, ysim = DV) %>%
binless() %>%
vpcstats()
plot_binless_vpc <- plot(binless_vpc, legend.position = "none") +
ggplot2::ggtitle("Binless")
# Create a binning VPC plot with binning method set to be "jenks"
binned_vpc <- observed(obs_data, x = IVAR, yobs = DV) %>%
simulated(sim_data, ysim = DV) %>%
binning(bin = "jenks") %>%
vpcstats()
plot_binned_vpc <- plot(binned_vpc) +
ggplot2::ggtitle("Binning")
## Put these two plots side-by-side
egg::ggarrange(plot_binned_vpc,
plot_binless_vpc,
nrow = 2, ncol = 1,
top = "VPC Comparison"
)