Example: Derived columns ([derived] and [output])

The [derived] block adds computed columns to sdtab after the fit. The [output] block echoes individual PK parameters and covariates into sdtab that would not be there otherwise. Neither block affects estimation — they are pure post-processing.

Model

library(ferx)

ex  <- ferx_example("warfarin_derived")
ferx_model_show(ex$model)
# model: warfarin_derived.ferx 
# One-compartment oral PK model (warfarin) -- [derived] and [output] demo

[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
  omega ETA_V  ~ 0.04
  omega ETA_KA ~ 0.30

  sigma PROP_ERR ~ 0.02 (sd)

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

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

[error_model]
  DV ~ proportional(PROP_ERR)

[derived]
  # Per-row: elimination rate and half-life
  KE     = CL / V
  T_HALF = 0.6931472 / KE

  # Which dosing day (tau = 24 h)
  DAY      = floor(TAFD / 24) + 1
  TAU_TIME = TAFD mod 24

  # Subject-level aggregates (one scalar per subject, repeated across rows)
  CMAX       = max(IPRED)
  TMAX       = tmax(IPRED)
  # 1e-10 absorbs floating-point residuals when TAD is mathematically 0
  CTROUGH    = min(IPRED, TAD < 1e-10)
  CMAX_D1    = max(IPRED, TAFD < 24)
  CMAX_D14   = max(IPRED, TAFD >= 312 && TAFD < 336)

  # AUC over first 72 h (fine internal grid, 500 steps)
  AUC_0_72   = integral(IPRED, from=0, to=72)

  # Periodic AUC: one value per 24-h dosing window
  AUC_TAU    = integral(IPRED, window=24, anchor=0, step=0.1)

  # DV-based AUC (observation times only -- no interpolation)
  AUC_DV_72  = integral(DV, from=0, to=72)

[output]
  # Echo individual PK parameters (ETAs already automatic)
  CL V KA

[fit_options]
  method     = foce
  maxiter    = 300
  covariance = true

The model adds twelve derived columns covering all three computation kinds:

Column Kind Description
KE, T_HALF, DAY, TAU_TIME Per-row Evaluated at each observation time
CMAX, TMAX, CTROUGH, CMAX_D1, CMAX_D14 Aggregate One value per subject (last two NaN for single-dose data)
AUC_0_72, AUC_TAU, AUC_DV_72 Integral Trapezoidal / periodic / DV-based AUC

[output] then adds CL, V, and KA (individual EBE parameter values).

Fit

fit <- ferx_fit(ex$model, ex$data, verbose = FALSE)
print(fit)
============================================================
 NONLINEAR MIXED EFFECTS MODEL ESTIMATION
============================================================
 Model: warfarin_derived         Dataset: warfarin
 Method: FOCE | Gradient: ANALYTIC (DUAL2) | Subjects: 10 | Obs: 110

 STATUS: CONVERGED   102 iterations   1.5s
 OFV: -280.3640    AIC: -266.3640    BIC: -247.4606

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

THETA
------------------------------------------------------------
Parameter            Estimate           SE       %RSE
----------------------------------------------------
TVCL                 0.132970     0.006628        5.0
TVV                  7.730695     0.234062        3.0
TVKA                 0.725187     0.125303       17.3

OMEGA  (between-subject variability)
------------------------------------------------------------
  ETA_CL                   [log-normal]  = 0.028585  CV% = 17.0  SE = 0.012790
  ETA_V                    [log-normal]  = 0.009575  CV% = 9.8  SE = 0.004294
  ETA_KA                   [log-normal]  = 0.348959  CV% = 64.6  SE = 0.160968

SIGMA  (residual error)
------------------------------------------------------------
  PROP_ERR         [proportional] = 0.010749  (var = 0.000116, CV% = 1.1)  SE = 0.000942  [initial specified as SD]

SHRINKAGE
------------------------------------------------------------
 ETA_CL: 0.0%   ETA_V: 0.1%   ETA_KA: 0.2%   EPS: 16.1%

DIAGNOSTICS
------------------------------------------------------------
 Covariance: computed   Cond: 2.7   DW: 2.61 [negative autocorrelation]   IWRES lag-1 r: -0.365

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

SETTINGS  (model file / call-time override)
------------------------------------------------------------
  method                       foce  [model only]
  maxiter                      300  [model only]
  covariance                   true  [model only]

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

sdtab columns

names(fit$sdtab)
 [1] "ID"        "TIME"      "DV"        "PRED"      "IPRED"     "CWRES"    
 [7] "IWRES"     "EBE_OFV"   "N_OBS"     "TAFD"      "TAD"       "CL"       
[13] "V"         "KA"        "KE"        "T_HALF"    "DAY"       "TAU_TIME" 
[19] "CMAX"      "TMAX"      "CTROUGH"   "CMAX_D1"   "CMAX_D14"  "AUC_0_72" 
[25] "AUC_TAU"   "AUC_DV_72"

Per-row columns

KE and T_HALF depend only on the subject’s EBE CL/V, so they are constant per subject but appear on every row. DAY increments with each 24-h window.

head(fit$sdtab[, c("ID", "TIME", "TAFD", "KE", "T_HALF", "DAY")], 12)
   ID  TIME  TAFD         KE   T_HALF DAY
1   1   0.5   0.5 0.01655816 41.86136   1
2   1   1.0   1.0 0.01655816 41.86136   1
3   1   2.0   2.0 0.01655816 41.86136   1
4   1   4.0   4.0 0.01655816 41.86136   1
5   1   8.0   8.0 0.01655816 41.86136   1
6   1  12.0  12.0 0.01655816 41.86136   1
7   1  24.0  24.0 0.01655816 41.86136   2
8   1  48.0  48.0 0.01655816 41.86136   3
9   1  72.0  72.0 0.01655816 41.86136   4
10  1  96.0  96.0 0.01655816 41.86136   5
11  1 120.0 120.0 0.01655816 41.86136   6
12  2   0.5   0.5 0.01860709 37.25177   1

Aggregate columns

CMAX, TMAX, and AUC_0_72 are the same on every row of a subject:

unique(fit$sdtab[, c("ID", "CMAX", "TMAX", "AUC_0_72")])
    ID     CMAX TMAX AUC_0_72
1    1 11.38329    4 510.6520
12   2 12.10572    8 544.1912
23   3 11.01643    4 514.5164
34   4 13.56179    8 594.6227
45   5 13.82101    4 637.5880
56   6 11.70240    2 521.3665
67   7 10.32414    8 441.9933
78   8 11.77892    4 467.5516
89   9 11.04441    4 563.9460
100 10 11.09372    8 533.1617

Filtered aggregates — CMAX_D1 vs CMAX

CMAX_D1 = max(IPRED, TAFD < 24) restricts the maximum to day-1 observations only. Comparing it against the overall CMAX shows how the peak shifts as the drug distributes:

unique(fit$sdtab[, c("ID", "CMAX", "CMAX_D1", "TMAX")])
    ID     CMAX  CMAX_D1 TMAX
1    1 11.38329 11.38329    4
12   2 12.10572 12.10572    8
23   3 11.01643 11.01643    4
34   4 13.56179 13.56179    8
45   5 13.82101 13.82101    4
56   6 11.70240 11.70240    2
67   7 10.32414 10.32414    8
78   8 11.77892 11.77892    4
89   9 11.04441 11.04441    4
100 10 11.09372 11.09372    8

The model also declares CTROUGH = min(IPRED, TAD < 1e-10) to capture the pre-dose trough, and CMAX_D14 = max(IPRED, TAFD >= 312 && TAFD < 336) for the steady-state day-14 peak. Both are NaN here because the warfarin dataset is a single-dose study (no repeated doses, data ends at 120 h) — TAD never resets to ≈ 0 at an observation time, and no observations fall in the 312–336 h window. These columns are designed for multi-dose designs. For a working trough example see Example: ADDL/multiple-dose.

Periodic AUC (AUC_TAU)

AUC_TAU = integral(IPRED, window=24, anchor=0, step=0.1) produces one value per 24-h dosing window. It changes across windows as the concentration profile evolves:

fit$sdtab[fit$sdtab$ID == fit$sdtab$ID[1],
          c("TIME", "TAFD", "AUC_TAU")]
    TIME  TAFD   AUC_TAU
1    0.5   0.5 233.55469
2    1.0   1.0 233.55469
3    2.0   2.0 233.55469
4    4.0   4.0 233.55469
5    8.0   8.0 233.55469
6   12.0  12.0 233.55469
7   24.0  24.0 165.76633
8   48.0  48.0 111.40623
9   72.0  72.0  74.87255
10  96.0  96.0  50.31944
11 120.0 120.0  40.41758

DV-based AUC (AUC_DV_72)

AUC_DV_72 = integral(DV, from=0, to=72) uses the observed values at each observation time rather than IPRED. The step= argument is ignored for DV-based integrals (no interpolation between observations):

unique(fit$sdtab[, c("ID", "AUC_0_72", "AUC_DV_72")])
    ID AUC_0_72 AUC_DV_72
1    1 510.6520  506.8519
12   2 544.1912  540.0808
23   3 514.5164  510.2423
34   4 594.6227  593.2185
45   5 637.5880  632.7343
56   6 521.3665  516.7186
67   7 441.9933  440.5478
78   8 467.5516  464.2751
89   9 563.9460  563.1022
100 10 533.1617  530.3337

[output] — individual parameter columns

CL, V, and KA appear in sdtab because of the [output] block. Without it, only the mandatory minimum columns plus [derived] names would be present:

unique(fit$sdtab[, c("ID", "CL", "V", "KA")])
    ID        CL        V        KA
1    1 0.1367378 8.258032 1.1738301
12   2 0.1350286 7.256836 0.5366661
23   3 0.1297238 8.467318 0.9441843
34   4 0.1291865 6.258011 0.3906890
45   5 0.1052014 6.846145 1.3118066
56   6 0.1315967 8.294638 2.8192265
67   7 0.1776121 8.230884 0.4165787
78   8 0.1738693 7.593123 0.7786906
89   9 0.1022804 8.490377 0.8951078
100 10 0.1251060 8.035877 0.4909804

See also