Inter-Occasion Variability

Overview

Inter-occasion variability (IOV) captures variability in PK parameters that changes between dosing occasions within the same subject — for example, day 1 vs day 7 clearance after a multi-dose study.

In the ferx DSL, IOV is expressed with the kappa keyword. Kappas are analogous to ETAs, but they are re-drawn for each occasion rather than being fixed per subject.

The individual parameter on occasion \(k\) for subject \(i\):

\[CL_{ik} = \text{TVCL} \times \exp(\eta_i^{CL} + \kappa_{ik}^{CL})\]

where \(\eta_i^{CL} \sim N(0, \omega_{CL}^2)\) is the between-subject component (constant across occasions) and \(\kappa_{ik}^{CL} \sim N(0, \omega_{IOV}^2)\) is the occasion-specific component.

Dataset requirements

Add an OCC column (integer occasion index per row). Convention: OCC = 1 for the first occasion, OCC = 2 for the second, etc.

ID  TIME  DV   EVID  AMT  OCC
 1   0.0  NA     1    70    1
 1   1.0  5.2    0     0    1
 ...
 1  24.0  NA     1    70    2     <- second dose, new occasion
 1  25.0  4.8    0     0    2

The kappa keyword (diagonal IOV)

Declare IOV parameters with kappa in [parameters]:

[parameters]
  theta TVCL(0.2, 0.001, 10.0)
  theta TVV(10.0, 0.1, 500.0)
  theta TVKA(1.5, 0.01, 50.0)

  omega ETA_CL ~ 0.09   # between-subject variance (constant per subject)
  omega ETA_V  ~ 0.04
  omega ETA_KA ~ 0.30

  kappa KAPPA_CL ~ 0.01  # IOV variance (re-drawn each occasion)

  sigma PROP_ERR ~ 0.02

Reference KAPPA_CL in [individual_parameters] just like an ETA:

[individual_parameters]
  CL = TVCL * exp(ETA_CL + KAPPA_CL)
  V  = TVV  * exp(ETA_V)
  KA = TVKA * exp(ETA_KA)

Enable the IOV column in [fit_options]:

[fit_options]
  method     = foce
  iov_column = OCC
  covariance = false

Fitting an IOV model

ferx ships two bundled IOV examples:

Example Method Notes
warfarin_iov FOCE Diagonal kappa on CL
warfarin_iov_saem SAEM Same structural model fit with SAEM
ex_iov  <- ferx_example("warfarin_iov")
fit_iov <- ferx_fit(ex_iov$model, ex_iov$data, method = "foce",
                    verbose = FALSE)
Mu-referencing detected for: ETA_CL, ETA_KA, ETA_V

The printed fit shows a dedicated OMEGA_IOV (inter-occasion variability) section with per-kappa CV%, SE, and shrinkage, followed by per-occasion shrinkage in a --- Shrinkage by occasion --- sub-table:

print(fit_iov)
============================================================
 NONLINEAR MIXED EFFECTS MODEL ESTIMATION
============================================================
 Model: warfarin_iov             Dataset: warfarin_iov
 Method: FOCE | Gradient: FD | Subjects: 10 | Obs: 220

 STATUS: CONVERGED   314 iterations   9.4s
 OFV: 263.3865    AIC: 279.3865    BIC: 306.5355

MODEL STRUCTURE (auto-derived)
------------------------------------------------------------
  Structural:  1-cpt oral  (TVCL, TVV, TVKA)
  IIV:         ETA_CL, ETA_V, ETA_KA
  IOV:         KAPPA_CL
  Residual:    proportional

THETA
------------------------------------------------------------
Parameter            Estimate           SE       %RSE
----------------------------------------------------
TVCL                 0.237869     0.018501        7.8
TVV                  8.609892     0.390722        4.5
TVKA                 3.144902     0.923031       29.4

OMEGA  (between-subject variability)
------------------------------------------------------------
  ETA_CL                   [log-normal]  = 0.165782  CV% = 42.5  SE = 0.104478
  ETA_V                    [log-normal]  = 0.013723  CV% = 11.8  SE = 0.007995
  ETA_KA                   [log-normal]  = 1.302141  CV% = 163.6  SE = 0.924616

OMEGA_IOV  (inter-occasion variability)
------------------------------------------------------------
  KAPPA_CL = 0.063194  (CV% = 25.5)  SE = 0.029893  Shrinkage = 35.5%
  --- Shrinkage by occasion ---
  Occasion 1: KAPPA_CL 21.5%
  Occasion 2: KAPPA_CL 53.5%

SIGMA  (residual error)
------------------------------------------------------------
  PROP_ERR         [proportional] = 0.175093  (var = 0.030658, CV% = 17.5)  SE = 0.016939  [initial specified as SD]

SHRINKAGE
------------------------------------------------------------
 ETA_CL: 14.5%   ETA_V: 21.0%   ETA_KA: 14.5%   EPS: 2.1%

DIAGNOSTICS
------------------------------------------------------------
 Covariance: computed   Cond: 19.1   DW: 0.50 [positive autocorrelation]   IWRES lag-1 r: 0.738

RUN INFO
------------------------------------------------------------
 Gradient (requested): auto   (used: fd)
 ferx v0.1.6 (core v0.1.6)

SETTINGS  (model file / call-time override)
------------------------------------------------------------
  method                       foce  [model only]
  iov_column                   OCC  [model only]
  covariance                   false  [model only]

------------------------------------------------------------
 1 warning   1 info  --  call ferx_warnings(fit) for details
============================================================

The kappa omega is stored separately from fit_iov$omega:

fit_iov$model_structure$iov   # names of kappa parameters
[1] "KAPPA_CL"
fit_iov$shrinkage_kappa       # per-kappa shrinkage (pooled across occasions)
 KAPPA_CL 
0.3549592 
fit_iov$shrinkage_kappa_by_occ  # per-occasion shrinkage data frame
  occ  KAPPA_CL
1   1 0.2150276
2   2 0.5352706

shrinkage_kappa_by_occ is a data frame with one row per occasion and one column per kappa parameter. The occ column holds a 1-based occasion index:

# Print the per-occasion shrinkage table
fit_iov$shrinkage_kappa_by_occ
#   occ  KAPPA_CL
# 1   1    0.182
# 2   2    0.153
# (values are illustrative; run the model to get exact numbers)

High shrinkage (> 30%) on a particular occasion indicates that the data for that occasion are too sparse to estimate per-occasion kappa EBEs reliably. If shrinkage is high on all occasions, consider removing IOV from that parameter or using a sparser model (fewer kappas).

SAEM with IOV

warfarin_iov_saem uses the same DSL with method = saem in [fit_options]. SAEM estimates kappa parameters directly — no FOCE pre-fit is required:

ex_saem  <- ferx_example("warfarin_iov_saem")
fit_saem <- ferx_fit(ex_saem$model, ex_saem$data)

Correlated IOV: block_kappa

When IOV on multiple parameters is correlated (e.g. CL and V co-vary across occasions), use block_kappa:

[parameters]
  ...
  block_kappa (KAPPA_CL, KAPPA_V) = [0.02, 0.005, 0.01]

The three values are the lower-triangle of the 2×2 covariance matrix (column-major order):

Position Value Meaning
[1] 0.02 Variance of KAPPA_CL
[2] 0.005 Covariance of KAPPA_CL and KAPPA_V
[3] 0.01 Variance of KAPPA_V

Both kappas are used in [individual_parameters] the same way:

[individual_parameters]
  CL = TVCL * exp(ETA_CL + KAPPA_CL)
  V  = TVV  * exp(ETA_V  + KAPPA_V)

Interpreting IOV results

Partitioning variability

The total clearance variance (on the log scale) is:

\[\text{Var}(\log CL_i) = \omega_{CL}^2 + \omega_{IOV}^2\]

The fraction of total variability attributable to IOV:

omega_bsv <- fit_iov$omega["ETA_CL", "ETA_CL"]
omega_iov <- fit_iov$omega_iov["KAPPA_CL", "KAPPA_CL"]
cat(sprintf("BSV: %.1f%%   IOV: %.1f%%   of total log-scale variance\n",
            100 * omega_bsv / (omega_bsv + omega_iov),
            100 * omega_iov / (omega_bsv + omega_iov)))

IOV shrinkage

Two shrinkage summaries are available after fitting:

  • fit$shrinkage_kappa — pooled shrinkage (scalar per kappa parameter)
  • fit$shrinkage_kappa_by_occ — per-occasion shrinkage data frame (one row per occasion, one column per kappa)

High shrinkage (> 30%) means the data cannot reliably estimate per-occasion kappas — consider removing IOV from that parameter. The per-occasion table is especially useful when occasions differ in sampling density: an early rich-sampling occasion may have low shrinkage while a later sparse occasion has high shrinkage, pointing to where the IOV is not supported by the data.

When to add IOV

Add IOV when:

  • The study has multiple distinct dosing occasions with washout between them
  • You see residual correlation in ETA-vs-time plots that suggests within-subject variation across occasions
  • A model comparison (OFV difference) shows significant improvement with IOV included

Do not add IOV to studies with sparse sampling where the data cannot support additional random effects.