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 Architecture Overview
21.1 Class hierarchy
PKNCA uses four S3 classes: PKNCAconc and PKNCAdose feed PKNCAdata, and pk.nca() wraps the result into 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(...)toPKNCAdata()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 = TRUEinPKNCAconc() - 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 = TRUEin theiradd.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.