Estimates the terminal elimination rate constant \(\lambda_z\) for each profile by log-linear regression, and derives from it the terminal half-life, the area under the curve extrapolated to infinity, and the percentage of that area that was extrapolated rather than observed.

derive_terminal_phase(
  pc,
  by = c("USUBJID", "PARAMCD"),
  conc = "AVAL",
  time = "AFRLT",
  method = c("linear", "lin_up_log_down"),
  min_points = 3L,
  max_pct_extrap = 20,
  tol = 1e-04
)

Arguments

pc

Concentration-time data: one record per subject, analyte and timepoint. The output of make_adpc() is a valid input.

by

Character vector of grouping variables. One output record is produced per combination.

conc

Name of the concentration column in pc.

time

Name of the time column in pc, in hours since first dose.

method

AUC method for the observed part: "linear" (default) or "lin_up_log_down". See derive_exposure_metrics().

min_points

Minimum number of points in the terminal regression. Must be at least 3, because an adjusted R-squared needs a residual degree of freedom.

max_pct_extrap

Percentage of AUCIFO above which AUCPEFL is "Y". Default 20.

tol

Adjusted R-squared tolerance for the best-fit tie-break.

Value

An eradam_terminal object: a tibble::tibble with one row per by group.

Details

This is the non-compartmental half of an exposure analysis that derive_exposure_metrics() deliberately does not do: that function computes only what can be read off the observed points (CMAX, TMAX, CMIN, CTROUGH, AUCLST), and every parameter here needs a fitted model.

How lambda z is chosen

The window is selected by the best fit rule used by non-compartmental analysis software:

  1. Candidate points are the quantifiable observations (conc > 0) at times strictly after TMAX. TMAX itself and everything earlier is excluded, so the absorption phase cannot contaminate the slope.

  2. Every terminal window of min_points (default 3, the conventional minimum) up to all candidate points is regressed as \(\ln C = a + b\,t\).

  3. Among the windows whose adjusted R-squared is within tol (default 0.0001) of the best value, the one using the most points is taken. Comparing adjusted rather than raw R-squared is what stops the rule from always preferring the shortest window.

  4. \(\lambda_z = -b\). If the fitted slope is not negative there is no elimination phase to describe and the result is NA.

The fit is reported with the estimate, never on its own: LAMZNPT (points used), LAMZLL/LAMZUL (the time window), R2 and R2ADJ. A reviewer can then judge the fit rather than trust it. When no acceptable fit exists, LAMZ and everything derived from it are NA and LAMZNRS states why in words — there is no silently bad number.

Arithmetic

Let CLST be the last positive concentration, at time TLST, and CLSTP \(= \exp(a + b\,TLST)\) its value predicted by the terminal fit.

  • AUCLST — observed area to TLST, by the same trapezoidal method as derive_exposure_metrics().

  • AUCIFO \(= AUCLST + CLST / \lambda_z\) (observed variant).

  • AUCIFP \(= AUCLST + CLSTP / \lambda_z\) (predicted variant). Both are returned; neither is "the" answer, and a study should state which it uses.

  • AUCPEO, AUCPEP — percentage of AUCIFO / AUCIFP contributed by the extrapolated tail, \(100 (AUCIF - AUCLST) / AUCIF\).

  • AUCPEFL"Y" when AUCPEO exceeds max_pct_extrap (default 20, the conventional acceptance threshold), otherwise "N". The value is still returned; the flag says do not rely on it.

Conventions

LAMZ, LAMZHL, LAMZLL, LAMZUL, LAMZNPT, R2, R2ADJ, AUCIFO, AUCIFP, AUCPEO, AUCPEP, CLST, TLST and AUCLST are CDISC PPTESTCD parameter codes, used here with their CDISC meanings. CLSTP, AUCPEFL and LAMZNRS are this package's names, as is the wide one-row-per-profile layout — CDISC would carry these as one record per parameter in PP/ADPP.

The best-fit selection rule, the exclusion of TMAX, the three-point minimum and the 20% extrapolation threshold are the accepted pharmacometric conventions, not this package's inventions. The tol = 1e-4 tie-break constant is the value non-compartmental analysis software uses.

Limits

AUCLST here runs to the last positive concentration, which is the non-compartmental convention. derive_exposure_metrics() runs its AUCLST over every non-missing record. The two agree unless a profile has trailing zero concentrations — which it will if you ran make_adpc() with blq = "zero" — and then this one is the standard value and the other includes the run-down to zero.

Everything in a by group is treated as one profile. On a multiple-dose study that is usually not what you want: the shipped fixture doses at 0 h and 24 h, so TMAX for 10 of its 24 subjects falls after the second dose and only two terminal points remain — those 10 correctly return NA with the reason rather than a slope fitted through two points. Subset to the dosing interval you mean before calling.

Not implemented: steady-state parameters (AUCTAU, CAVG, CLSS, accumulation and fluctuation), clearance and volume (CL, CLF, VZ, VZF, MRT), moment curves (AUMC, VSS), sparse-sampling and Bailer-type variance estimation, and any manual override of the LAMZ window — the best-fit rule chooses it or nothing does, which a regulatory NCA usually needs to be able to overrule. If you need those, run the profile through a validated NCA package; this is an open implementation of published rules, not validated software.

Output structure

VariableMeaningSource
USUBJIDUnique subject identifierpc (grouping variable)
PARAMCDAnalyte codepc (grouping variable)
NOBSNumber of usable observations in the profileDerived
TLSTTime of the last positive (quantifiable) concentrationDerived from time
CLSTLast positive (quantifiable) concentration, observedDerived from conc
AUCLSTArea under the curve to TLST, by the trapezoidal methodDerived
CLSTPConcentration at TLST predicted by the terminal fitDerived from the lambda z regression
LAMZTerminal elimination rate constant (1/time); NA when no acceptable fitDerived by log-linear regression
LAMZHLTerminal half-life, log(2) / LAMZDerived
LAMZNPTNumber of points used in the terminal regressionDerived
LAMZLLLower time limit of the terminal regression windowDerived
LAMZULUpper time limit of the terminal regression windowDerived
R2R-squared of the terminal regressionDerived
R2ADJAdjusted R-squared of the terminal regression (the value the window was chosen on)Derived
AUCIFOAUC extrapolated to infinity using the observed CLSTDerived
AUCIFPAUC extrapolated to infinity using the predicted CLSTPDerived
AUCPEOPercentage of AUCIFO that was extrapolatedDerived
AUCPEPPercentage of AUCIFP that was extrapolatedDerived
AUCPEFL"Y" when AUCPEO exceeds max_pct_extrap (default 20%)Derived
LAMZNRSReason LAMZ is NA, in words; NA when LAMZ was estimatedDerived

See also

derive_exposure_metrics() for the observed metrics and derive_partial_auc() for an area over a stated window.

Examples

# a synthetic mono-exponential decay: C = 100 * exp(-0.1 * t)
t <- c(0, 1, 2, 4, 8, 12, 24)
mono <- data.frame(
  USUBJID = "X-001", PARAMCD = "DRUGX",
  AFRLT = t, AVAL = 100 * exp(-0.1 * t)
)

tp <- derive_terminal_phase(mono)
tp[, c("LAMZ", "LAMZHL", "LAMZNPT", "R2ADJ", "AUCIFO", "AUCPEO")]
#> # A tibble: 1 × 6
#>    LAMZ LAMZHL LAMZNPT R2ADJ AUCIFO AUCPEO
#>   <dbl>  <dbl>   <int> <dbl>  <dbl>  <dbl>
#> 1   0.1   6.93       6     1  1030.   8.81
# lambda z recovers 0.1, half-life log(2) / 0.1, AUCIFO the analytic 100 / 0.1

# a profile with too few points after TMAX yields NA and says why
short <- data.frame(
  USUBJID = "X-002", PARAMCD = "DRUGX",
  AFRLT = c(0, 1, 2), AVAL = c(1, 10, 5)
)
derive_terminal_phase(short)$LAMZNRS
#> [1] "Only 1 quantifiable point after TMAX (1); 3 required."

# on the shipped fixtures
pc <- eradam_example("pc")
derive_terminal_phase(pc[pc$PARAMCD == "DRUGX", ])
#> 
#> ── eradam terminal-phase parameters ────────────────────────────────────────────
#>  24 profile(s) | AUC method: linear | min points: 3
#>  Lambda z estimated for 14/24 profile(s)
#> ! 1 profile(s): Only 2 quantifiable points after TMAX (28); 3 required.
#> ! 1 profile(s): Only 2 quantifiable points after TMAX (27.993); 3 required.
#> ! 1 profile(s): Only 2 quantifiable points after TMAX (28.036); 3 required.
#> ! 1 profile(s): Only 2 quantifiable points after TMAX (27.951); 3 required.
#> ! 1 profile(s): Only 2 quantifiable points after TMAX (28.06); 3 required.
#> ! 1 profile(s): Only 2 quantifiable points after TMAX (28.022); 3 required.
#> ! 1 profile(s): Only 2 quantifiable points after TMAX (28.05); 3 required.
#> ! 1 profile(s): Only 2 quantifiable points after TMAX (28.029); 3 required.
#> ! 1 profile(s): Only 2 quantifiable points after TMAX (27.97); 3 required.
#> ! 1 profile(s): Only 2 quantifiable points after TMAX (28.014); 3 required.
#>  LAMZHL: median 6.519 (range 4.251 to 9.53)
#>  No profile extrapolates more than 20% of AUCIFO
#> # A tibble: 24 × 20
#>    USUBJID  PARAMCD  NOBS  TLST  CLST AUCLST CLSTP    LAMZ LAMZHL LAMZNPT LAMZLL
#>    <chr>    <chr>   <int> <dbl> <dbl>  <dbl> <dbl>   <dbl>  <dbl>   <int>  <dbl>
#>  1 ERADAM0… DRUGX      11  48.0  15.6  6619.  15.7  0.148    4.69       3   28.0
#>  2 ERADAM0… DRUGX      11  48.0  64.6 11468.  63.2  0.101    6.85       3   28.1
#>  3 ERADAM0… DRUGX      11  48.0  89.9 11919.  NA   NA       NA         NA   NA  
#>  4 ERADAM0… DRUGX      11  48.1  53.7 10329.  NA   NA       NA         NA   NA  
#>  5 ERADAM0… DRUGX      11  48.0  30.8  6868.  31.4  0.114    6.09       3   28.0
#>  6 ERADAM0… DRUGX      11  48.0  30.6  6645.  NA   NA       NA         NA   NA  
#>  7 ERADAM0… DRUGX      11  48.0  41.9  8033.  NA   NA       NA         NA   NA  
#>  8 ERADAM0… DRUGX      11  48.1  82.3 12978.  83.2  0.0898   7.72       3   28.0
#>  9 ERADAM0… DRUGX      11  48.0  92.5 19621.  93.9  0.108    6.44       3   28.0
#> 10 ERADAM0… DRUGX      11  48.0 252.  30823. 259.   0.0727   9.53       3   28.0
#> # ℹ 14 more rows
#> # ℹ 9 more variables: LAMZUL <dbl>, R2 <dbl>, R2ADJ <dbl>, AUCIFO <dbl>,
#> #   AUCIFP <dbl>, AUCPEO <dbl>, AUCPEP <dbl>, AUCPEFL <chr>, LAMZNRS <chr>