Parameter Transforms

Overview

How an ETA is incorporated into the individual parameter determines the distribution of that parameter across subjects. ferx supports three common transforms: log-normal, additive, and logit-normal.

Log-normal (default)

[individual_parameters]
  CL = TVCL * exp(ETA_CL)

The individual clearance CL_i = TVCL × exp(η_i) where η_i ~ N(0, ω²).

This parameterisation:

  • Keeps CL_i > 0 by construction
  • The geometric mean of CL across subjects equals TVCL
  • CV% grows with ω²; ferx reports √(exp(ω²) − 1) × 100

When to use: clearance, volume, rate constants — parameters that are always positive and whose variability is naturally multiplicative.

CV% formula

For a log-normal ETA with omega variance \(\omega^2\), ferx reports:

\[\text{CV\%} = \sqrt{e^{\omega^2} - 1} \times 100\]

omega_sq <- c(0.04, 0.09, 0.16, 0.25, 0.50)
data.frame(
  omega_sq = omega_sq,
  cv_pct   = sqrt(exp(omega_sq) - 1) * 100
)
  omega_sq   cv_pct
1     0.04 20.20168
2     0.09 30.68783
3     0.16 41.65464
4     0.25 53.29404
5     0.50 80.54324

Additive ETA

[individual_parameters]
  TLAG = TVTLAG + ETA_TLAG

The individual lag time TLAG_i = TVTLAG + η_i where η_i ~ N(0, ω²). The omega variance is now in the same units as the parameter (e.g., hours²).

When to use:

  • Parameters that can be zero or negative by physiology (some effect parameters)
  • Lag times constrained near zero — the additive form avoids inflation of CV% at small values
  • Log-scale parameters (take log(TVCL) as the typical value)

Example from the bundled warfarin_additive_eta model:

ex <- ferx_example("warfarin_additive_eta")
ferx_model_show(ex$model)
# model: warfarin_additive_eta.ferx 
# One-compartment oral PK — additive ETA on lag time
#
# Demonstrates additive ETA parameterisation: the individual lag time is
# modelled as TLAG + ETA_TLAG (normal distribution), rather than the usual
# log-normal TLAG * exp(ETA).  This is appropriate when the parameter has a
# natural additive variability structure and cannot go negative by definition
# (or is bounded away from zero by the individual's biology).
#
# Lag time is routed via the `lagtime=` PK parameter (NONMEM-style `alag=`
# is also accepted) — the variable name on the left of the assignment can
# be anything (here, `TLAG`).
#
# Use ferx_example("warfarin_additive_eta") to get file paths.

[parameters]
  theta TVCL(0.134, 0.001, 10.0)
  theta TVV(8.1, 0.1, 500.0)
  theta TVKA(1.0, 0.01, 50.0)
  theta TVTLAG(0.5, 0.0, 5.0)        # typical lag time (h)

  omega ETA_CL   ~ 0.07              # BSV on CL (log-normal)
  omega ETA_V    ~ 0.02              # BSV on V  (log-normal)
  omega ETA_TLAG ~ 0.04              # BSV on TLAG (additive, variance in h^2)

  sigma PROP_ERR ~ 0.01 (sd)

[individual_parameters]
  CL   = TVCL  * exp(ETA_CL)
  V    = TVV   * exp(ETA_V)
  KA   = TVKA
  TLAG = TVTLAG + ETA_TLAG

[structural_model]
  pk one_cpt_oral(cl=CL, v=V, ka=KA, lagtime=TLAG)

[error_model]
  DV ~ proportional(PROP_ERR)

[fit_options]
  method     = focei
  maxiter    = 300
  covariance = true
[individual_parameters]
  CL   = TVCL  * exp(ETA_CL)
  V    = TVV   * exp(ETA_V)
  KA   = TVKA
  TLAG = TVTLAG + ETA_TLAG      # additive — variance in h²
fit_add <- ferx_fit(ex$model, ex$data, method = "focei", covariance = FALSE)
Warning: Model file [fit_options] sets `covariance = true` but ferx_fit()
argument overrides it with `false`. The call-time value will be used.
Mu-referencing detected for: ETA_CL, ETA_TLAG, ETA_V
summary(fit_add)
--------------------------------------------------
ferx NLME Fit Summary
--------------------------------------------------
Model:     warfarin_additive_eta
Dataset:   warfarin_additive_eta
Converged: YES
Method:    FOCEI
Gradient:  auto (requested) -> analytic (Dual2) (used)
ferx v0.1.6 (core v0.1.6)

Settings (model file / call-time override):
  method                       focei  [model only]
  maxiter                      300  [model only]
  covariance                   true  [model only]

Structure: 1-cpt oral  (TVCL, TVV, TVKA, TVTLAG)  |  IIV: ETA_CL, ETA_V, ETA_TLAG  |  IOV: none  |  Residual: proportional

OFV: -10.6451  AIC: 5.3549  BIC: 26.9587
Subjects: 10  Obs: 110  Params: 8  Iter: 672
Shrinkage: ETA1=1.1%  ETA2=4.3%  ETA3=3.0% 
EPS shrinkage: 13.6%
DW: 1.19 [positive autocorrelation]
lag-1 r: 0.356
Covariance: not requested  |  Wall time: 0.4s

Warnings:
  * mu-ref: ETA_CL, ETA_TLAG, ETA_V 
  * Positive IWRES autocorrelation detected (Durbin-Watson = 1.19). Structural model may be missing dynamics. Consider a transit absorption model, additional compartment, or IOV on ka/F. 
  * Shapiro-Wilk flags possible non-normal distribution for 1 ETA: ETA_TLAG (p=0.0113) 
  * 31 EBE fallback(s) 
--------------------------------------------------

Logit-normal for bounded parameters

For parameters bounded to (0, 1) — bioavailability, fraction absorbed — use the logit transformation:

[individual_parameters]
  F = inv_logit(logit(THETA_F) + ETA_F)

THETA_F is specified on the (0, 1) scale in [parameters] with bounds (0.001, 0.999). ferx maps it to the logit scale internally so the optimiser works on an unbounded parameter space.

The individual bioavailability is:

\[F_i = \text{logit}^{-1}(\text{logit}(\theta_F) + \eta_i) = \frac{e^{\text{logit}(\theta_F) + \eta_i}}{1 + e^{\text{logit}(\theta_F) + \eta_i}}\]

CV% is not meaningful for logit-normal parameters because the distribution is asymmetric. Report the omega variance on the logit scale and the 5th–95th percentile range:

theta_f <- 0.80   # typical bioavailability
omega_f <- 0.10   # omega variance on logit scale

logit    <- function(p) log(p / (1 - p))
inv_logit <- function(x) 1 / (1 + exp(-x))

logit_typical <- logit(theta_f)
lo <- inv_logit(logit_typical - 1.96 * sqrt(omega_f))
hi <- inv_logit(logit_typical + 1.96 * sqrt(omega_f))
cat(sprintf("F: typical = %.2f, 95%% range = [%.2f, %.2f]\n", theta_f, lo, hi))
F: typical = 0.80, 95% range = [0.68, 0.88]

Example using the bundled warfarin_logit_f model:

ex_lf <- ferx_example("warfarin_logit_f")
fit_lf <- ferx_fit(ex_lf$model, ex_lf$data, method = "focei", covariance = FALSE)
Warning: Model file [fit_options] sets `covariance = true` but ferx_fit()
argument overrides it with `false`. The call-time value will be used.
Mu-referencing detected for: ETA_CL, ETA_V
summary(fit_lf)
--------------------------------------------------
ferx NLME Fit Summary
--------------------------------------------------
Model:     warfarin_logit_f
Dataset:   warfarin_logit_f
Converged: YES
Method:    FOCEI
Gradient:  auto (requested) -> analytic (Dual2) (used)
ferx v0.1.6 (core v0.1.6)

Settings (model file / call-time override):
  method                       focei  [model only]
  maxiter                      300  [model only]
  covariance                   true  [model only]

Structure: 1-cpt oral  (TVCL, TVV, TVKA, THETA_F)  |  IIV: ETA_CL, ETA_V, ETA_F  |  IOV: none  |  Residual: proportional

OFV: 129.4554  AIC: 145.4554  BIC: 167.0593
Subjects: 10  Obs: 110  Params: 8  Iter: 439
Shrinkage: ETA1=65.7%  ETA2=10.8%  ETA3=13.3% 
EPS shrinkage: 7.2%
DW: 0.34 [positive autocorrelation]
lag-1 r: 0.802
Covariance: not requested  |  Wall time: 0.3s

Warnings:
  * mu-ref: ETA_CL, ETA_V 
  * Positive IWRES autocorrelation detected (Durbin-Watson = 0.34). Structural model may be missing dynamics. Consider a transit absorption model, additional compartment, or IOV on ka/F. 
  * 31 EBE fallback(s) 
--------------------------------------------------

Choosing the right transform

Situation Parameterisation
Always positive, multiplicative variability TVCL * exp(ETA_CL)
Near-zero or could go negative TVTLAG + ETA_TLAG
Bounded (0, 1) inv_logit(logit(THETA_F) + ETA_F)
Log-scale typical value exp(log_TVCL + ETA_CL) (equivalent to log-normal)

Omega display in summary()

The summary() output shows omega on the variance scale. To convert manually:

fit <- ferx_fit(ferx_example("warfarin")$model,
                ferx_example("warfarin")$data)
Mu-referencing detected for: ETA_CL, ETA_KA, ETA_V
omega_diag <- diag(fit$omega)
cv_pct     <- sqrt(exp(omega_diag) - 1) * 100
names(cv_pct) <- names(omega_diag)
round(cv_pct, 1)
ETA_CL  ETA_V ETA_KA 
  17.0    9.8   64.0