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 > 0by 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
ETA_CL ETA_V ETA_KA
17.0 9.8 64.0