Skip to contents

The purpose of this vignette is to demonstrate how to:

  • Create an xpose_data object from NLME model execution directory/files
  • Utilize functions from the xpose package to generate basic GOF plots
  • Extend xpose plot functions with ggplot2
  • Explore application of covariate model diagnostics functions, only available in Certara.Xpose.NLME
  • Extract GOF data from the xpose_data object and explore usage of ggcertara

Note: The corresponding R script used in this example can be found in RsNLME_Examples/TwoCptIVBolus_FitBaseModel_CovariateSearch_VPC_BootStrapping.R. See RsNLME Example Scripts.


Data

We will be using the built-in pkData from the Certara.RsNLME package for the following examples. pkData is a pharmacokinetic dataset containing 16 subjects with single bolus dose.

library(Certara.RsNLME)
library(dplyr)
library(magrittr)

pkData <- Certara.RsNLME::pkData

glimpse(pkData)
#> Rows: 112
#> Columns: 8
#> $ Subject    <dbl> 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3,…
#> $ Nom_Time   <dbl> 0, 1, 1, 2, 4, 8, 24, 0, 1, 1, 2, 4, 8, 24, 0, 1, 1, 2, 4, …
#> $ Act_Time   <dbl> 0.00, 0.26, 1.10, 2.10, 4.13, 8.17, 23.78, 0.00, 0.37, 0.83…
#> $ Amount     <dbl> 25000, NA, NA, NA, NA, NA, NA, 25000, NA, NA, NA, NA, NA, N…
#> $ Conc       <dbl> 2010.0, 1330.0, 565.0, 216.0, 180.0, 120.0, 16.9, 850.0, 79…
#> $ Age        <dbl> 22, 22, 22, 22, 22, 22, 22, 46, 46, 46, 46, 46, 46, 46, 52,…
#> $ BodyWeight <dbl> 73, 73, 73, 73, 73, 73, 73, 79, 79, 79, 79, 79, 79, 79, 63,…
#> $ Gender     <chr> "male", "male", "male", "male", "male", "male", "male", "fe…

Model Building and Execution

Before exploring usage of Certara.Xpose.NLME and evaulating our covariate model diagnostic functions, we will first:

  • Build and execute our base model
  • Copy the base model and accept parameter estimates
  • Add continuous and categorical covariates to the new model
  • Execute the new covariate model

For a detailed explanation on model building and execution in Certara.RsNLME visit here.

Build and execute base model

# Create model
baseModel <- pkmodel(
  numCompartments = 2, data = pkData,
  ID = "Subject", Time = "Act_Time", A1 = "Amount", CObs = "Conc",
  modelName = "TwCpt_IVBolus_FOCE_ELS"
)

# Update parameters
baseModel <- baseModel %>%
  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)

# Fit model
baseFitJob <- fitmodel(baseModel)

Copy model and accept parameter estimates


covariateModel <- copyModel(baseModel,
  acceptAllEffects = TRUE,
  modelName = "TwCpt_IVBolus_SelectedCovariateModel_FOCE_ELS"
)

Add covariates to new model and fit

covariateModel <- covariateModel %>%
  addCovariate(covariate = c(Sex = "Gender"), effect = c("V", "Cl"), type = "Categorical", levels = c(0, 1), labels = c("female", "male")) %>%
  addCovariate(covariate = "Age", effect = c("V", "Cl")) %>%
  addCovariate(covariate = c(BW = "BodyWeight"), effect = c("V", "Cl"), center = "Value", centerValue = 70)


covariateJob <- fitmodel(covariateModel)

Create xpose_data object from RsNLME model output

We can create our xpose_data object from our RsNLME output by supplying the output directory where our model execution files have been saved. If the model in your R environment has been executed previously, you may pass the folder path from model@modelInfo@workingDir directly to the dir argument of the xposeNlme() function.

library(Certara.Xpose.NLME)
library(xpose)

xpdb <- xposeNlme(dir = covariateModel@modelInfo@workingDir)

class(xpdb)
#> [1] "xpose_data" "uneval"

List available variables

We will use the list_vars() function from xpose to print out useful information about our xpose_data object.

list_vars(xpdb)
#> 
#> List of available variables for problem no. 1 
#>  - Subject identifier (id)               : ID
#>  - Dependent variable (dv)               : DV
#>  - Independent variable (idv)            : IVAR
#>  - DV identifier (dvid)                  : ObsName
#>  - Dose amount (amt)                     : Amount
#>  - Event identifier (evid)               : EVID
#>  - Model typical predictions (pred)      : PRED
#>  - Model individual predictions (ipred)  : IPRED
#>  - Model parameter (param)               : V, Cl, V2, Cl2
#>  - Eta (eta)                             : nV, nCl, nCl2
#>  - Residuals (res)                       : IRES, IWRES, WRES, CWRES, PCWRES, NPDE
#>  - Categorical covariates (catcov)       : Sex
#>  - Continuous covariates (contcov)       : Age, BW
#>  - Not attributed (na)                   : Subject, Nom_Time, Act_Time, Conc, BodyWeight, Gender, WhichReset, TIME, TAD, PREDSE, Weight, PPRED, CdfPCWRES, CdfDV, NPD, WhichDose

Covariate Model Diagnostics

Covariate model diagnostics plot types vary given the covariate type (continuous or categorical). Continuous covariates are plotted in a scatter plot, while categorical covariates in a box plot.

The following “xpose-like” covariate model diagnostics functions are available in Certara.Xpose.Nlme:

  • res_vs_cov()
  • prm_vs_cov()
  • eta_vs_cov()

The above functions follow xpose conventions, including usage of the type argument to specify the type of plot attributes and usage of the ... ellipses to allow pass through geom arguments from ggplot2.

Note: The returned plot from xpose and Certara.Xpose.Nlme is of class ggplot, therefore functions from ggplot2 can be applied to the plot for further customized styling.

Residuals vs Covariate

Let’s look inside the data and preview which residuals are available for plotting.

library(dplyr)
library(xpose)

gofData <- get_data(xpdb)

gofData %>%
  select(ends_with("RES"))
#> # A tibble: 112 × 6
#>       IRES  IWRES   WRES  CWRES    PCWRES CdfPCWRES
#>      <dbl>  <dbl>  <dbl>  <dbl>     <dbl>     <dbl>
#>  1  263.    0.945  1.18   1.13  3.30e-151 3.30e-151
#>  2  120.    0.622  0.670  0.649 3.30e-151 3.30e-151
#>  3  110.    1.51   1.08   1.07  3.30e-151 3.30e-151
#>  4  -24.1  -0.631 -1.02  -1.08  3.30e-151 3.30e-151
#>  5   19.7   0.772  0.480  0.539 3.30e-151 3.30e-151
#>  6   14.6   0.869  0.251  0.280 3.30e-151 3.30e-151
#>  7   -4.75 -1.38  -1.67  -1.92  3.30e-151 3.30e-151
#>  8 -251.   -1.43  -1.59  -1.69  3.30e-151 3.30e-151
#>  9   31.6   0.261  0.391  0.394 3.30e-151 3.30e-151
#> 10 -141.   -1.76  -1.32  -1.26  3.30e-151 3.30e-151
#> # ℹ 102 more rows

CWRES vs Continuous Covariate

library(Certara.Xpose.NLME)

res_vs_cov(xpdb, covariate = "Age", res = "CWRES", type = "ps")
#> `geom_smooth()` using formula = 'y ~ x'
#> `geom_smooth()` using formula = 'y ~ x'

A residuals vs continuous covariate plot showing CWRES vs Age.

CWRES vs Categorical Covariate

library(Certara.Xpose.NLME)

res_vs_cov(xpdb, covariate = "Sex", res = "CWRES", type = "b")

A residuals vs categorical covariate plot showing CWRES vs Sex.

Extending with ggplot2

Add reference line

library(Certara.Xpose.NLME)
library(ggplot2)

res_vs_cov(xpdb, covariate = "BW", res = "CWRES", type = "ps") +
  geom_hline(yintercept = 0, linetype = "dashed", lwd = 1, color = "red") +
  theme_bw2()
#> `geom_smooth()` using formula = 'y ~ x'
#> `geom_smooth()` using formula = 'y ~ x'

A residuals vs covariate plot showing CWRES vs Bodyweight with a reference line added.

ETAs vs Covariates

ETAs vs Continuous Covariate


eta_vs_cov(xpdb, covariate = "Age", type = "ps")
#> `geom_smooth()` using formula = 'y ~ x'
#> `geom_smooth()` using formula = 'y ~ x'

An eta vs continuous covariate plot with age as the covariate.

ETAs vs Categorical Covariate


eta_vs_cov(xpdb, covariate = "Sex", type = "b")

A residuals vs categorical covariate plot with sex as the covariate.

Structural Parameters vs Covariates

Structural Parameters vs Continuous Covariate


prm_vs_cov(xpdb, covariate = "BW", type = "ps")
#> `geom_smooth()` using formula = 'y ~ x'
#> `geom_smooth()` using formula = 'y ~ x'

A figure showing four structural parameter vs continuous covariate plots (Cl, Cl2, V, V2) with bodyweight as the covariate.

Structural Parameters vs Categorical Covariate


prm_vs_cov(xpdb, covariate = "Sex", type = "b")

A figure showing four structural parameter vs categorical covariate plots (Cl, Cl2, V, V2) with sex as the covariate.

Note: We can remove the variable V2 from the plots, which has a fixed value, by including the argument drop_fixed = TRUE.

Using ggcertara

ggcertara is an R package to provide a standardized look for plots employed by pharmacometricians. It provides a ggplot2 theme, color palette, and a collection of plotting functions for basic goodness-of-fit diagnostic plots.

Goodness-of-fit (GOF) data can easily be extracted from the xpose_data object in order to utilize plot functions from ggcertara.

For ggcertara installation and usage details, visit the following link.

To learn more about the standardized set of basic goodness-of-fit diagnostic plots available in ggcertara, explore the following vignette.

library(ggcertara)

gofData <- xpose::get_data(xpdb)

colnames(gofData) <- tolower(colnames(gofData)) # ggcertara expects lower case column names

glimpse(gofData)
#> Rows: 112
#> Columns: 40
#> $ subject    <int> 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3,…
#> $ nom_time   <int> 0, 1, 1, 2, 4, 8, 24, 0, 1, 1, 2, 4, 8, 24, 0, 1, 1, 2, 4, …
#> $ act_time   <dbl> 0.00, 0.26, 1.10, 2.10, 4.13, 8.17, 23.78, 0.00, 0.37, 0.83…
#> $ amount     <int> 25000, NA, NA, NA, NA, NA, NA, 25000, NA, NA, NA, NA, NA, N…
#> $ conc       <dbl> 2010.0, 1330.0, 565.0, 216.0, 180.0, 120.0, 16.9, 850.0, 79…
#> $ bodyweight <int> 73, 73, 73, 73, 73, 73, 73, 79, 79, 79, 79, 79, 79, 79, 63,…
#> $ gender     <chr> "male", "male", "male", "male", "male", "male", "male", "fe…
#> $ id         <fct> 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3,…
#> $ whichreset <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
#> $ time       <dbl> 0.00, 0.26, 1.10, 2.10, 4.13, 8.17, 23.78, 0.00, 0.37, 0.83…
#> $ sex        <fct> 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,…
#> $ age        <dbl> 22, 22, 22, 22, 22, 22, 22, 46, 46, 46, 46, 46, 46, 46, 52,…
#> $ bw         <dbl> 73, 73, 73, 73, 73, 73, 73, 79, 79, 79, 79, 79, 79, 79, 63,…
#> $ v          <dbl> 14.30656, 14.30656, 14.30656, 14.30656, 14.30656, 14.30656,…
#> $ cl         <dbl> 7.618555, 7.618555, 7.618555, 7.618555, 7.618555, 7.618555,…
#> $ v2         <dbl> 41.45072, 41.45072, 41.45072, 41.45072, 41.45072, 41.45072,…
#> $ cl2        <dbl> 13.19652, 13.19652, 13.19652, 13.19652, 13.19652, 13.19652,…
#> $ ivar       <dbl> 0.00, 0.26, 1.10, 2.10, 4.13, 8.17, 23.78, 0.00, 0.37, 0.83…
#> $ tad        <dbl> 0.00, 0.26, 1.10, 2.10, 4.13, 8.17, 23.78, 0.00, 0.37, 0.83…
#> $ pred       <dbl> 1666.64000, 1167.83000, 460.68400, 254.87200, 175.01400, 11…
#> $ ipred      <dbl> 1747.4500, 1210.2300, 455.3340, 240.0880, 160.3180, 105.429…
#> $ dv         <dbl> 2010.0, 1330.0, 565.0, 216.0, 180.0, 120.0, 16.9, 850.0, 79…
#> $ ires       <dbl> 262.550000, 119.766000, 109.666000, -24.088300, 19.682000, …
#> $ predse     <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
#> $ weight     <dbl> 3.27484e-07, 6.82749e-07, 4.82326e-06, 1.73483e-05, 3.89077…
#> $ iwres      <dbl> 0.945081, 0.622479, 1.514980, -0.631098, 0.772233, 0.869342…
#> $ wres       <dbl> 1.17603000, 0.67027600, 1.07858000, -1.02127000, 0.48004400…
#> $ cwres      <dbl> 1.12795000, 0.64915100, 1.07142000, -1.08164000, 0.53875800…
#> $ ppred      <dbl> 3.3e-151, 3.3e-151, 3.3e-151, 3.3e-151, 3.3e-151, 3.3e-151,…
#> $ pcwres     <dbl> 3.3e-151, 3.3e-151, 3.3e-151, 3.3e-151, 3.3e-151, 3.3e-151,…
#> $ cdfpcwres  <dbl> 3.3e-151, 3.3e-151, 3.3e-151, 3.3e-151, 3.3e-151, 3.3e-151,…
#> $ npde       <dbl> 3.3e-151, 3.3e-151, 3.3e-151, 3.3e-151, 3.3e-151, 3.3e-151,…
#> $ cdfdv      <dbl> 3.3e-151, 3.3e-151, 3.3e-151, 3.3e-151, 3.3e-151, 3.3e-151,…
#> $ npd        <dbl> 3.3e-151, 3.3e-151, 3.3e-151, 3.3e-151, 3.3e-151, 3.3e-151,…
#> $ obsname    <fct> CObs, CObs, CObs, CObs, CObs, CObs, CObs, CObs, CObs, CObs,…
#> $ whichdose  <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,…
#> $ nv         <dbl> -0.04734556, -0.04734556, -0.04734556, -0.04734556, -0.0473…
#> $ ncl        <dbl> 0.06860499, 0.06860499, 0.06860499, 0.06860499, 0.06860499,…
#> $ ncl2       <dbl> -0.06537580, -0.06537580, -0.06537580, -0.06537580, -0.0653…
#> $ evid       <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…

Basic GOF

Use the gof() function to plot basic GOF plot with default panel options.

gof(gofData)

A figure showing six default goodness of fit plots.

You may select from different combinations of gof plots using the panels argument.

gof(gofData, panels = 3:6)

A figure showing panels three through six of the default goodness of fit plots.

Color Explorer

The ggcertara package contains a useful Shiny application to explore different color palettes and test how those with color blindness will perceive your selected colors.

First, create the plot and assign a return value.

myplot <- gof(gofData)

Next, pass the plot to the plotobj argument in run_colorexplorer().

run_colorexplorer(plotobj = myplot)

An image showing how you can use the ggcertara Shiny GUI to explore different color palettes for plots.

To simulate color blindness given the colors chosen in the palette, click the checkbox labeled “Simulate Color Blindness”.