21  Architecture Overview

21.1 Class hierarchy

PKNCA uses four S3 classes: PKNCAconc and PKNCAdose feed PKNCAdata, and pk.nca() wraps the result into PKNCAresults:

classDiagram
    class PKNCAconc {
        data: data.frame
        formula: conc~time|groups
        subject: column name
        sparse: logical
        units: concu/timeu
        exclude / exclude_half.life
    }
    class PKNCAdose {
        data: data.frame
        formula: dose~time|groups
        route: intravas./extravas.
        duration / rate
        units: doseu/timeu
    }
    class PKNCAdata {
        conc: PKNCAconc
        dose: PKNCAdose
        intervals: data.frame
        impute: method string
        units: pknca_units_table()
        options: named list
    }
    class PKNCAresults {
        result: data.frame
        data: PKNCAdata
    }

    PKNCAconc --> PKNCAdata
    PKNCAdose --> PKNCAdata
    PKNCAdata --> PKNCAresults

21.2 The interval column system

The parameter registry is the core extensibility mechanism. Every NCA parameter is registered as an interval column via add.interval.col().

# Inspect registration for auclast
cols <- get.interval.cols()
str(cols[["auclast"]])
List of 11
 $ FUN           : chr "pk.calc.auc.last"
 $ values        : logi [1:2] FALSE TRUE
 $ unit_type     : chr "auc"
 $ pretty_name   : chr "AUClast"
 $ desc          : chr "The area under the concentration time curve from the beginning of the interval to the last concentration above "| __truncated__
 $ sparse        : logi FALSE
 $ formalsmap    : list()
 $ depends       : NULL
 $ datatype      : chr "interval"
 $ pptestcd_cdisc: chr "AUCLST"
 $ pptest_cdisc  : chr "AUC to Last Nonzero Conc"

Each entry contains:

Field Purpose
FUN Name of the function that computes this parameter (character string, or NA for parameters populated by another function’s multi-column result)
values Allowed values in the interval column (typically c(FALSE, TRUE))
unit_type Unit category used for unit assignment and conversion
pretty_name Human-readable name used in summary tables
depends Character vector of parameters that must be computed first
desc Human-readable description
sparse Whether this parameter works for sparse PK
formalsmap Maps function arguments to interval/group/options data
datatype Calculation type; only "interval" is currently supported
pptestcd_cdisc CDISC PPTESTCD code (≥ 0.12.2)
pptest_cdisc CDISC PPTEST label (≥ 0.12.2)

21.3 formalsmap

formalsmap is how PKNCA passes the right data to each parameter function. Arguments can be sourced from four places:

Source type Example What it provides
Interval column conc, time Concentrations/times for the current interval
Other parameters auclast = "auclast" A previously computed NCA result
Interval bounds start, end The interval start/end times
Options options The full analysis options list

Individual option names (e.g. auc.method) cannot be mapped through formalsmap; only the whole options list can, and parameter functions read specific settings from it via PKNCA.choose.option().

21.4 Writing a custom parameter

Registering a new parameter is a single add.interval.col() call that names the computing function; PKNCA then derives execution order from depends, assigns units from unit_type, and collects results automatically. A minimal toy registration:

# Toy illustration: ratio of two built-in parameters
pk.calc.demo.ratio <- function(cmax, cmin) {
  cmax / cmin
}

add.interval.col(
  name        = "demo.ratio",
  FUN         = "pk.calc.demo.ratio",  # must be a character string, not the function object
  unit_type   = "unitless",
  pretty_name = "Demo ratio (Cmax/Cmin)",
  desc        = "Toy example: Cmax divided by Cmin",
  depends     = c("cmax", "cmin")
)

# The registry now contains it alongside the built-in parameters
str(get.interval.cols()[["demo.ratio"]])
List of 11
 $ FUN           : chr "pk.calc.demo.ratio"
 $ values        : logi [1:2] FALSE TRUE
 $ unit_type     : chr "unitless"
 $ pretty_name   : chr "Demo ratio (Cmax/Cmin)"
 $ desc          : chr "Toy example: Cmax divided by Cmin"
 $ sparse        : logi FALSE
 $ formalsmap    : list()
 $ depends       : chr [1:2] "cmax" "cmin"
 $ datatype      : chr "interval"
 $ pptestcd_cdisc: chr "demo.ratio"
 $ pptest_cdisc  : chr "Toy example: Cmax divided by Cmin"

For the full walkthrough — parameters computed from raw concentration-time data, multi-output functions, and custom summary statistics — see the Writing Custom Parameters chapter and the package vignette Writing PKNCA Parameter Functions.

21.5 Options system

PKNCA’s options are stored in a package-level environment; PKNCA.options() returns them as a named list. It is accessed in several ways:

# 1. Read all options
PKNCA.options()
$adj.r.squared.factor
[1] 1e-04

$max.missing
[1] 0.5

$auc.method
[1] "lin up/log down"

$conc.na
[1] "drop"

$conc.blq
$conc.blq$first
[1] "keep"

$conc.blq$middle
[1] "drop"

$conc.blq$last
[1] "keep"


$debug
NULL

$first.tmax
[1] TRUE

$first.tmin
[1] TRUE

$allow.tmax.in.half.life
[1] FALSE

$keep_interval_cols
NULL

$min.hl.points
[1] 3

$min.span.ratio
[1] 2

$max.aucinf.pext
[1] 20

$min.hl.r.squared
[1] 0.9

$progress
[1] TRUE

$tau.choices
[1] NA

$single.dose.aucs
  start end auclast aucall aumclast aumcall aucint.last aucint.last.dose
1     0  24    TRUE  FALSE    FALSE   FALSE       FALSE            FALSE
2     0 Inf   FALSE  FALSE    FALSE   FALSE       FALSE            FALSE
  aucint.all aucint.all.dose aumcint.last aumcint.last.dose aumcint.all
1      FALSE           FALSE        FALSE             FALSE       FALSE
2      FALSE           FALSE        FALSE             FALSE       FALSE
  aumcint.all.dose    c0  cmax  cmin  tmax  tmin tlast tfirst clast.obs cl.last
1            FALSE FALSE FALSE FALSE FALSE FALSE FALSE  FALSE     FALSE   FALSE
2            FALSE FALSE  TRUE FALSE  TRUE FALSE FALSE  FALSE     FALSE   FALSE
  cl.all cl.int.all cl.int.last     f mrt.last mrt.all mrt.int.all mrt.int.last
1  FALSE      FALSE       FALSE FALSE    FALSE   FALSE       FALSE        FALSE
2  FALSE      FALSE       FALSE FALSE    FALSE   FALSE       FALSE        FALSE
  mrt.iv.last vss.last vss.iv.last vss.all vss.int.all vss.int.last   cav
1       FALSE    FALSE       FALSE   FALSE       FALSE        FALSE FALSE
2       FALSE    FALSE       FALSE   FALSE       FALSE        FALSE FALSE
  cav.int.last cav.int.all ctrough cstart   ptr  tlag deg.fluc swing  ceoi
1        FALSE       FALSE   FALSE  FALSE FALSE FALSE    FALSE FALSE FALSE
2        FALSE       FALSE   FALSE  FALSE FALSE FALSE    FALSE FALSE FALSE
  aucabove.predose.all aucabove.trough.all count_conc count_conc_measured
1                FALSE               FALSE      FALSE               FALSE
2                FALSE               FALSE      FALSE               FALSE
  totdose volpk    ae clr.last clr.obs clr.pred    fe ertlst ermax ertmax
1   FALSE FALSE FALSE    FALSE   FALSE    FALSE FALSE  FALSE FALSE  FALSE
2   FALSE FALSE FALSE    FALSE   FALSE    FALSE FALSE  FALSE FALSE  FALSE
  sparse_auclast sparse_auc_se sparse_auc_df sparse_aumclast sparse_aumc_se
1          FALSE         FALSE         FALSE           FALSE          FALSE
2          FALSE         FALSE         FALSE           FALSE          FALSE
  sparse_aumc_df time_above aucivlast aucivall aucivint.last aucivint.all
1          FALSE      FALSE     FALSE    FALSE         FALSE        FALSE
2          FALSE      FALSE     FALSE    FALSE         FALSE        FALSE
  aucivpbextlast aucivpbextall aucivpbextint.last aucivpbextint.all aumcivlast
1          FALSE         FALSE              FALSE             FALSE      FALSE
2          FALSE         FALSE              FALSE             FALSE      FALSE
  aumcivall aumcivint.last aumcivint.all half.life r.squared adj.r.squared
1     FALSE          FALSE         FALSE     FALSE     FALSE         FALSE
2     FALSE          FALSE         FALSE      TRUE     FALSE         FALSE
  lambda.z.corrxy lambda.z lambda.z.time.first lambda.z.time.last
1           FALSE    FALSE               FALSE              FALSE
2           FALSE    FALSE               FALSE              FALSE
  lambda.z.n.points clast.pred span.ratio tobit_residual adj_tobit_residual
1             FALSE      FALSE      FALSE          FALSE              FALSE
2             FALSE      FALSE      FALSE          FALSE              FALSE
  lambda.z.n.points_blq thalf.eff.last thalf.eff.iv.last kel.last kel.iv.last
1                 FALSE          FALSE             FALSE    FALSE       FALSE
2                 FALSE          FALSE             FALSE    FALSE       FALSE
  kel.all kel.int.all kel.int.last cl.iv.all cl.iv.last cl.ivint.all
1   FALSE       FALSE        FALSE     FALSE      FALSE        FALSE
2   FALSE       FALSE        FALSE     FALSE      FALSE        FALSE
  cl.ivint.last cl.sparse.last mrt.sparse.last mrt.iv.all mrt.ivint.all
1         FALSE          FALSE           FALSE      FALSE         FALSE
2         FALSE          FALSE           FALSE      FALSE         FALSE
  mrt.ivint.last vz.all vz.int.all vz.int.last vz.iv.all vz.iv.last
1          FALSE  FALSE      FALSE       FALSE     FALSE      FALSE
2          FALSE  FALSE      FALSE       FALSE     FALSE      FALSE
  vz.ivint.all vz.ivint.last vz.last vss.iv.all vss.ivint.all vss.ivint.last
1        FALSE         FALSE   FALSE      FALSE         FALSE          FALSE
2        FALSE         FALSE   FALSE      FALSE         FALSE          FALSE
  vss.sparse.last aucinf.obs aucinf.pred aumcinf.obs aumcinf.pred
1           FALSE      FALSE       FALSE       FALSE        FALSE
2           FALSE       TRUE       FALSE       FALSE        FALSE
  aucint.inf.obs aucint.inf.obs.dose aucint.inf.pred aucint.inf.pred.dose
1          FALSE               FALSE           FALSE                FALSE
2          FALSE               FALSE           FALSE                FALSE
  aumcint.inf.obs aumcint.inf.obs.dose aumcint.inf.pred aumcint.inf.pred.dose
1           FALSE                FALSE            FALSE                 FALSE
2           FALSE                FALSE            FALSE                 FALSE
  aucivinf.obs aucivinf.pred aucivpbextinf.obs aucivpbextinf.pred aumcivinf.obs
1        FALSE         FALSE             FALSE              FALSE         FALSE
2        FALSE         FALSE             FALSE              FALSE         FALSE
  aumcivinf.pred aucpext.obs aucpext.pred kel.iv.all kel.ivint.all
1          FALSE       FALSE        FALSE      FALSE         FALSE
2          FALSE       FALSE        FALSE      FALSE         FALSE
  kel.ivint.last kel.sparse.last cl.obs cl.pred cl.int.inf.obs cl.int.inf.pred
1          FALSE           FALSE  FALSE   FALSE          FALSE           FALSE
2          FALSE           FALSE  FALSE   FALSE          FALSE           FALSE
  cl.iv.obs cl.iv.pred mrt.obs mrt.pred mrt.int.inf.obs mrt.int.inf.pred
1     FALSE      FALSE   FALSE    FALSE           FALSE            FALSE
2     FALSE      FALSE   FALSE    FALSE           FALSE            FALSE
  mrt.iv.obs mrt.iv.pred mrt.md.obs mrt.md.pred vz.obs vz.pred vz.int.inf.obs
1      FALSE       FALSE      FALSE       FALSE  FALSE   FALSE          FALSE
2      FALSE       FALSE      FALSE       FALSE  FALSE   FALSE          FALSE
  vz.int.inf.pred vz.iv.obs vz.iv.pred vz.sparse.last vss.obs vss.pred
1           FALSE     FALSE      FALSE          FALSE   FALSE    FALSE
2           FALSE     FALSE      FALSE          FALSE   FALSE    FALSE
  vss.iv.obs vss.iv.pred vss.md.obs vss.md.pred vss.int.inf.obs
1      FALSE       FALSE      FALSE       FALSE           FALSE
2      FALSE       FALSE      FALSE       FALSE           FALSE
  vss.int.inf.pred cav.int.inf.obs cav.int.inf.pred thalf.eff.obs
1            FALSE           FALSE            FALSE         FALSE
2            FALSE           FALSE            FALSE         FALSE
  thalf.eff.pred thalf.eff.iv.obs thalf.eff.iv.pred kel.obs kel.pred kel.iv.obs
1          FALSE            FALSE             FALSE   FALSE    FALSE      FALSE
2          FALSE            FALSE             FALSE   FALSE    FALSE      FALSE
  kel.iv.pred kel.int.inf.obs kel.int.inf.pred auclast.dn aucall.dn
1       FALSE           FALSE            FALSE      FALSE     FALSE
2       FALSE           FALSE            FALSE      FALSE     FALSE
  aucinf.obs.dn aucinf.pred.dn aumclast.dn aumcall.dn aumcinf.obs.dn
1         FALSE          FALSE       FALSE      FALSE          FALSE
2         FALSE          FALSE       FALSE      FALSE          FALSE
  aumcinf.pred.dn cmax.dn cmin.dn clast.obs.dn clast.pred.dn cav.dn ctrough.dn
1           FALSE   FALSE   FALSE        FALSE         FALSE  FALSE      FALSE
2           FALSE   FALSE   FALSE        FALSE         FALSE  FALSE      FALSE
  clr.last.dn clr.obs.dn clr.pred.dn demo.ratio
1       FALSE      FALSE       FALSE      FALSE
2       FALSE      FALSE       FALSE      FALSE

$allow_partial_missing_units
[1] FALSE

$hl_method
[1] "log-linear"

$tobit_n_points_penalty
[1] 0

$tobit_optim_control
list()
# 2. Read a specific option
PKNCA.options("min.hl.points")
[1] 3
# 3. Set globally — save the old value first so you can restore it
old_val <- PKNCA.options("min.hl.points")[[1]]
PKNCA.options(min.hl.points = 4)
PKNCA.options("min.hl.points")
[1] 4
# 4. Restore by passing the saved value as a named argument
PKNCA.options(min.hl.points = old_val)
PKNCA.options("min.hl.points")
[1] 3

Prefer passing options = list(...) to PKNCAdata() over mutating global options. Global changes persist across analyses in the same session.

21.6 Sparse PK

Sparse PK (one or two samples per subject, common in preclinical or pediatric studies) is handled via a parallel code path:

  • Set sparse = TRUE in PKNCAconc()
  • Concentrations are pooled across subjects per timepoint
  • Mean concentration-time profile is computed, then NCA is run on that profile
  • Supported parameters are flagged with sparse = TRUE in their add.interval.col() registration

For the full workflow, see the Sparse NCA chapter and the vignette Sparse NCA Calculations.

21.7 Key functions

All of these are exported except where marked internal:

Function Purpose
get.interval.cols() Return full parameter registry
PKNCA:::sort_interval_cols() Topological sort → execution order (internal, unexported)
pk.nca.interval() Compute all parameters for one interval/group
pknca_units_table() Build unit assignment/conversion table
PKNCA_impute_method_*() Built-in imputation methods
pk.calc.auc.last() Core AUC calculation (trapezoidal)
pk.calc.half.life() λz regression with best-fit selection

This overview covers the surface. The business rules (002-pk.business.rules.R) and the validation framework (v60-PKNCA-validation.Rmd) in the PKNCA sources are richer topics worth exploring further.