Skip to contents

Introduction

Originally, posologyr models were R lists; posologyr still uses this format internally, but it’s often more useful to use the rxode2 syntax, or even to import a NONMEM model using the nonmem2rx package.

This article describes the structure of classical posologyr models. It illustrates how to define them from published population models.

Structure

A posologyr model is a named R list of the following items:

ppk_model
A rxode2 model implementing the structural population model with the individual model (i.e. the model of inter-individual variability) and the covariates
error_model
A function of the residual error model, alternatively a named list of functions for multiple endpoints model vignette("multiple_endpoints")
theta
A named vector of the population estimates of the fixed effects parameters (called THETAs, following NONMEM terminology)
omega
A named square variance-covariance matrix of the population parameters inter-individual variability
sigma
The estimates of the parameters of the residual error model
pi_matrix
Optional. A named square variance-covariance matrix of the population parameters inter-occasion variability
covariates
A character vector of the covariates of the model

Definition of a prior model through an example

The model in this example is a two-compartment ppk model of vancomycin derived from a retrospective study with a cohort of over 1,800 patients (doi:10.1097/FTD.0000000000000490).

ppk_model

A model defined in the rxode2 mini-language. posologyr needs a structural model, defined with either differential or algebraic equations, and an individual model. Depending on model type, naming conventions are more or less strict:

Single endpoint model (e.g. most pharmacokinetic models)

  • The concentration in the central compartment must be named Cc.

Multiple endpoints model (eg. PK-PD, parent-metabolite, blood-urine…)

  • The names of the endpoints are flexible, but they must be consistent with the names of the error models and their parameters.

The differential function d/dt(AUC) = Cc; is needed for the optimization function poso_dose_auc().

ppk_model   = rxode2::rxode({
    centr(0) = 0;
    TVCl  = THETA_Cl*(CLCREAT/120)^0.8*(0.7^DIAL);
    TVVc  = THETA_Vc*(WT/70)          *(0.5^DIAL);
    TVVp  = THETA_Vp;
    TVQ   = THETA_Q;
    Cl    = TVCl*exp(ETA_Cl);
    Vc    = TVVc*exp(ETA_Vc);
    Vp    = TVVp*exp(ETA_Vp);
    Q     = TVQ;
    ke    = Cl/Vc;
    k12   = Q/Vc;
    k21   = Q/Vp;
    Cc    = centr/Vc;
    d/dt(centr)  = - ke*centr - k12*centr + k21*periph;
    d/dt(periph) =            + k12*centr - k21*periph;
    d/dt(AUC)    =   Cc;
  })
#> using C compiler: ‘gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0’

error_model

A function of the residual error model, taking two arguments: the simulated concentrations, and a vector sigma of the estimates of the parameters for the residual error model.

error_model <- function(f,sigma){           #additive model if sigma[2] == 0
  g <- sigma[1]^2 + (sigma[2]^2)*(f^2)      #proportional model if sigma[1] == 0
  return(sqrt(g))
}

For multiple endpoints models, error_model must be a named list with a function for each endpoint vignette("multiple_endpoints").

error_model = list(
    first_endpoint = function(f,sigma){
      g <- sigma[1]^2 + (sigma[2]^2)*(f^2)
      return(sqrt(g))
    },
    second_endpoint = function(f,sigma){
      g <- sigma[1]^2 + (sigma[2]^2)*(f^2)
      return(sqrt(g))
    }
  )

To obtain individual estimations for multiple endpoints, consistency in the naming convention must be maintained across the following:

  • The dataset (using the column DVID).
  • The residual error models (stored in a named list).
  • The standard deviation of the residual error models (stored in a named list, see sigma).

As many residual error models as desired can be defined. Each model defined in the named list error_models must have its counterpart in the named list sigma, and the names must match those defined in the DVID column of the dataset.

theta

The estimations of the parameters for the fixed effects of the model (THETA), in a named vector. The names must match the names used in ppk_model.

theta = c(THETA_Cl=4.5, THETA_Vc=58.4, THETA_Vp=38.4, THETA_Q=6.5)

omega

The variance-covariance matrix of the random effects (ETA) for the individual model. A symmetric matrix. The names must match the names used in ppk_model. An easy way to define it is using lotri::lotri().

The estimates of the variances of the random effects can be given under different parameterizations depending on the authors.

  • Standard deviation (SD): the square root of the variance, as returned by Monolix
  • Coefficient of variation (CV): calculated as sqrt(exp(SD^2)-1), the variance can be computed back with log((CV^2)+1)
  • Full covariance matrix: the easiest to reuse, but less common in the literature

In the case of the vancomycin model, the estimates of between subject variability (BSV) are given as CV%. They must be converted to variances prior to their inclusion in omega.

Parameter CV% (from the article) Variance = SD^2
BSV on CL 39.8 0.147
BSV on Vc 81.6 0.510
BSV on Vp 57.1 0.282
omega = lotri::lotri({ETA_Cl + ETA_Vc + ETA_Vp + ETA_Q ~
                          c(0.147,
                            0    ,  0.510 ,
                            0    ,  0     ,   0.282,
                            0    ,  0     ,   0    ,    0)})

The estimates of covariance (off-diagonal) are sometimes given as coefficients of correlation between ETAs. The covariance between ETA_a and ETA_b can be computed with the following product: standard_deviation(ETA_a) * standard_deviation(ETA_b) * correlation(ETA_a and ETA_b).

In this example, all covariances are equal to zero.

sigma

The estimates of the parameters for the residual error model on the standard deviation scale, either in a vector:

sigma       = c(additive_a = 3.4, proportional_b = 0.227)

in a matrix:

sigma       = lotri::lotri({prop + add ~ c(0.227,0.0,3.4)})

or in a named list (for multiple endpoints):

sigma       = list(
    first_endpoint=c(additive_a = 0.144, proportional_b = 0.15),
    second_endpoint=c(additive_a = 3.91, proportional_b = 0.0)
    )

depending on the residual error model.

pi_matrix

Optional: only needed for models with inter-occasion variability (IOV). The variance-covariance matrix of the random effects (KAPPA) for the IOV. As for the omega matrix, the names must match the names used in ppk_model. An easy way to define it is using lotri::lotri().

pi_matrix = lotri::lotri({KAPPA_Cl + KAPPA_Vc ~
      c(0.1934626,
        0.00     ,  0.05783106)})

covariates

The names of every covariate defined in ppk_model, in a character vector.

covariates  = c("CLCREAT","WT","DIAL")

Full model

The posologyr model is the list of all these objects. Note: this model does not include inter-occasion variability, so the pi_matrix is omitted.

mod_vancomyin_Goti2018 <- list(
  ppk_model   = rxode2::rxode({
     centr(0) = 0;
    TVCl  = THETA_Cl*(CLCREAT/120)^0.8*(0.7^DIAL);
    TVVc  = THETA_Vc*(WT/70)          *(0.5^DIAL);
    TVVp  = THETA_Vp;
    TVQ   = THETA_Q;
    Cl    = TVCl*exp(ETA_Cl);
    Vc    = TVVc*exp(ETA_Vc);
    Vp    = TVVp*exp(ETA_Vp);
    Q     = TVQ;
    ke    = Cl/Vc;
    k12   = Q/Vc;
    k21   = Q/Vp;
    Cc    = centr/Vc;
    d/dt(centr)  = - ke*centr - k12*centr + k21*periph;
    d/dt(periph) =            + k12*centr - k21*periph;
    d/dt(AUC)    =   Cc;
  }),
  error_model = function(f,sigma){
    g <- sigma[1] + sigma[2]*f
    return(g)
  },
  theta = c(THETA_Cl=4.5, THETA_Vc=58.4, THETA_Vp=38.4,THETA_Q=6.5),
  omega = lotri::lotri({ETA_Cl + ETA_Vc + ETA_Vp + ETA_Q ~
                          c(0.147,
                            0    ,  0.510 ,
                            0    ,  0     ,   0.282,
                            0    ,  0     ,   0    ,    0)}),
  sigma       = c(additive_a = 3.4, proportional_b = 0.227),
  covariates  = c("CLCREAT","WT","DIAL"))
#> using C compiler: ‘gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0’

Resulting R object

mod_vancomyin_Goti2018
#> $ppk_model
#> rxode2 NA model named rx_bce7176cfa18100af62e71ae38d3cd48 model ( ready). 
#> $state: centr, periph, AUC
#> $params: THETA_Cl, CLCREAT, DIAL, THETA_Vc, WT, THETA_Vp, THETA_Q, ETA_Cl, ETA_Vc, ETA_Vp
#> $lhs: TVCl, TVVc, TVVp, TVQ, Cl, Vc, Vp, Q, ke, k12, k21, Cc
#> 
#> $error_model
#> function(f,sigma){
#>     g <- sigma[1] + sigma[2]*f
#>     return(g)
#>   }
#> 
#> $theta
#> THETA_Cl THETA_Vc THETA_Vp  THETA_Q 
#>      4.5     58.4     38.4      6.5 
#> 
#> $omega
#>        ETA_Cl ETA_Vc ETA_Vp ETA_Q
#> ETA_Cl  0.147   0.00  0.000     0
#> ETA_Vc  0.000   0.51  0.000     0
#> ETA_Vp  0.000   0.00  0.282     0
#> ETA_Q   0.000   0.00  0.000     0
#> 
#> $sigma
#>     additive_a proportional_b 
#>          3.400          0.227 
#> 
#> $covariates
#> [1] "CLCREAT" "WT"      "DIAL"