18  Utility Functions

18.1 Overview

PKNCA exports several lower-level utility functions useful for data preparation, interval validation, and direct computation outside the full PKNCAdatapk.nca() pipeline.


18.2 BLQ concentration handling: clean.conc.blq()

Applies the BLQ-handling rule to a concentration vector before fitting. The full analysis pipeline applies this automatically, but clean.conc.blq() is useful for pre-processing or testing your own rule.

conc <- c(0.5, 0.3, 0,    0.1, 0,    0.0, 0.02)
time <- c(0,   1,   2,    3,   4,    5,   6   )

# Drop BLQ (0) values
clean.conc.blq(conc, time, conc.blq = "drop")
  conc time
1 0.50    0
2 0.30    1
4 0.10    3
7 0.02    6
# Replace BLQ with half the LLOQ (= 0.05 here)
clean.conc.blq(conc, time, conc.blq = 0.05)
  conc time
1 0.50    0
2 0.30    1
3 0.05    2
4 0.10    3
5 0.05    4
6 0.05    5
7 0.02    6

The conc.blq argument accepts: - "drop" — remove BLQ rows - "keep" — keep BLQ rows as-is - A number — substitute BLQ values with that number


18.3 NA concentration handling: clean.conc.na()

Removes or replaces NA concentrations. Internally called before AUC computation.

conc_na <- c(0.5, NA, 0.3, 0.1, NA)
time_na <- c(0,   1,   2,   3,   4)

# Default: drop NAs
clean.conc.na(conc_na, time_na, conc.na = "drop")
  conc time
1  0.5    0
3  0.3    2
4  0.1    3
# Replace NAs with 0
clean.conc.na(conc_na, time_na, conc.na = 0)
  conc time
1  0.5    0
2  0.0    1
3  0.3    2
4  0.1    3
5  0.0    4

18.4 Concentration interpolation / extrapolation: interp.extrap.conc()

Interpolates or extrapolates a concentration at a target time given a known concentration-time profile. Used internally when an interval boundary falls between two observed timepoints.

conc_ex <- c(10, 8, 5, 2.5, 1)
time_ex <- c(0,  1, 2, 4,   8)

# Interpolate at t = 3 (between t=2 and t=4)
interp.extrap.conc(conc = conc_ex, time = time_ex, time.out = 3)
[1] 3.535534
# Extrapolate beyond the last sample (t = 12)
interp.extrap.conc(conc = conc_ex, time = time_ex, time.out = 12)
[1] NA

The method (linear vs log-linear) follows the auc.method setting. Extrapolation uses the terminal log-linear slope (requires at least 3 points for λz estimation).


18.5 Direct half-life computation: pk.calc.half.life()

Runs the full curve-stripping algorithm on raw concentration-time data and returns the selected half-life, λz, and associated diagnostics. Useful for batch processing outside the standard pipeline.

subj1 <- as.data.frame(Theoph) |> filter(Subject == "1")

hl_result <- pk.calc.half.life(
  conc    = subj1$conc,
  time    = subj1$Time,
  options = PKNCA.options()
)

hl_result[c("half.life", "lambda.z", "r.squared", "lambda.z.n.points",
            "lambda.z.time.first", "span.ratio")]
  half.life lambda.z r.squared lambda.z.n.points lambda.z.time.first span.ratio
1  14.30438 0.048457 0.9999997                 3                9.05   1.071001

This is the same function called internally by pk.nca() when half.life = TRUE.


18.6 Direct AUC computation: pk.calc.auc.all()

Computes AUC (trapezoid rule) directly on a concentration-time vector. Returns the scalar AUC value.

pk.calc.auc.all(
  conc   = subj1$conc,
  time   = subj1$Time,
  method = "lin up/log down"
)
[1] 147.2347

Other direct AUC functions:

Function AUC type
pk.calc.auc.last() AUC to last measurable concentration
pk.calc.auc.all() AUC to last, treating BLQ as 0
pk.calc.auc.inf.obs() AUC extrapolated to ∞ (observed Clast)
pk.calc.auc.inf.pred() AUC extrapolated to ∞ (predicted Clast)

These are the building blocks behind every AUC parameter in the registry.


18.7 Validating interval specifications: check.interval.specification()

Validates an intervals data frame before passing it to PKNCAdata(). Returns the data frame with all parameter columns filled in (missing columns defaulted to FALSE).

my_intervals <- data.frame(
  start    = 0,
  end      = Inf,
  auclast  = TRUE,
  cmax     = TRUE
)

# Returns the full expanded interval spec
expanded <- check.interval.specification(my_intervals)

# Which parameters are requested?
names(expanded)[as.logical(expanded[1, ])]
[1] "end"     "auclast" "cmax"   
# Invalid specification — missing start column — raises an error
tryCatch(
  check.interval.specification(data.frame(end = Inf, auclast = TRUE)),
  error = function(e) cat("Error:", conditionMessage(e), "\n")
)
Error: Column(s) 'start' missing from interval specification 

This is useful for debugging custom interval definitions before running the full analysis.


18.8 Listing all available parameters

cols <- get.interval.cols()
cat("Total registered parameters:", length(cols), "\n")
Total registered parameters: 143 
# Show first few with their dependency lists
lapply(head(cols, 5), function(x) x$depends)
$start
NULL

$end
NULL

$auclast
NULL

$aucall
NULL

$aumclast
NULL
# All dose-normalized parameters
dn_params <- names(cols)[endsWith(names(cols), ".dn")]
cat("Dose-normalized parameters:\n")
Dose-normalized parameters:
cat(paste(" -", dn_params), sep = "\n")
 - auclast.dn
 - aucall.dn
 - aucinf.obs.dn
 - aucinf.pred.dn
 - aumclast.dn
 - aumcall.dn
 - aumcinf.obs.dn
 - aumcinf.pred.dn
 - cmax.dn
 - cmin.dn
 - clast.obs.dn
 - clast.pred.dn
 - cav.dn
 - ctrough.dn
 - clr.last.dn
 - clr.obs.dn
 - clr.pred.dn

18.9 Modifying PKNCAdose after creation: setRoute() and setDuration()

After creating a PKNCAdose object, you can change the route or infusion duration without rebuilding from scratch.

d_dose_u <- data.frame(subject = "1", dose = 100, time = 0)
o_dose_u <- PKNCAdose(d_dose_u, dose ~ time | subject, route = "extravascular")

# Change to intravascular
o_dose_iv <- setRoute(o_dose_u, route = "intravascular")
Found column named route, using it for the attribute of the same name.
# Add infusion duration (0.5 h = 30 min)
o_dose_inf <- setDuration(o_dose_iv, duration = 0.5)
Found column named duration, using it for the attribute of the same name.
# Verify
o_dose_inf$data[c("route", "duration")]
          route duration
1 intravascular      0.5

This is useful when you want to test how route assumption changes affect MRT or Vss calculations without rebuilding the full PKNCAdata object.


18.10 Getting parameter dependencies: get.parameter.deps()

Returns the upstream parameters that a given parameter depends on. Useful when debugging why a parameter is NA (a dependency may have failed).

get.parameter.deps("vss.obs")
[1] "vss.obs"
get.parameter.deps("half.life")
 [1] "adj_tobit_residual"    "adj.r.squared"         "aucinf.obs"           
 [4] "aucinf.obs.dn"         "aucinf.pred"           "aucinf.pred.dn"       
 [7] "aucint.inf.obs"        "aucint.inf.obs.dose"   "aucint.inf.pred"      
[10] "aucint.inf.pred.dose"  "aucivinf.obs"          "aucivinf.pred"        
[13] "aucivpbextinf.obs"     "aucivpbextinf.pred"    "aucpext.obs"          
[16] "aucpext.pred"          "aumcinf.obs"           "aumcinf.obs.dn"       
[19] "aumcinf.pred"          "aumcinf.pred.dn"       "cav.int.inf.obs"      
[22] "cav.int.inf.pred"      "cl.obs"                "cl.pred"              
[25] "clast.pred"            "clast.pred.dn"         "half.life"            
[28] "kel.iv.obs"            "kel.iv.pred"           "kel.obs"              
[31] "kel.pred"              "lambda.z"              "lambda.z.corrxy"      
[34] "lambda.z.n.points"     "lambda.z.n.points_blq" "lambda.z.time.first"  
[37] "lambda.z.time.last"    "mrt.iv.obs"            "mrt.iv.pred"          
[40] "mrt.md.obs"            "mrt.md.pred"           "mrt.obs"              
[43] "mrt.pred"              "r.squared"             "span.ratio"           
[46] "thalf.eff.iv.obs"      "thalf.eff.iv.pred"     "thalf.eff.obs"        
[49] "thalf.eff.pred"        "tobit_residual"        "vss.iv.obs"           
[52] "vss.iv.pred"           "vss.md.obs"            "vss.md.pred"          
[55] "vss.obs"               "vss.pred"              "vz.obs"               
[58] "vz.pred"              

18.11 Listing all current option values: PKNCA.options()

PKNCA.options() with no arguments returns the full named list of current settings:

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    c0  cmax  cmin  tmax  tmin tlast tfirst
1      FALSE           FALSE FALSE FALSE FALSE FALSE FALSE FALSE  FALSE
2      FALSE           FALSE FALSE  TRUE FALSE  TRUE FALSE FALSE  FALSE
  clast.obs cl.last cl.all     f mrt.last mrt.iv.last vss.last vss.iv.last
1     FALSE   FALSE  FALSE FALSE    FALSE       FALSE    FALSE       FALSE
2     FALSE   FALSE  FALSE FALSE    FALSE       FALSE    FALSE       FALSE
    cav cav.int.last cav.int.all ctrough cstart   ptr  tlag deg.fluc swing
1 FALSE        FALSE       FALSE   FALSE  FALSE FALSE FALSE    FALSE FALSE
2 FALSE        FALSE       FALSE   FALSE  FALSE FALSE FALSE    FALSE FALSE
   ceoi aucabove.predose.all aucabove.trough.all count_conc count_conc_measured
1 FALSE                FALSE               FALSE      FALSE               FALSE
2 FALSE                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 half.life
1          FALSE         FALSE              FALSE             FALSE     FALSE
2          FALSE         FALSE              FALSE             FALSE      TRUE
  r.squared adj.r.squared lambda.z.corrxy lambda.z lambda.z.time.first
1     FALSE         FALSE           FALSE    FALSE               FALSE
2     FALSE         FALSE           FALSE    FALSE               FALSE
  lambda.z.time.last lambda.z.n.points clast.pred span.ratio tobit_residual
1              FALSE             FALSE      FALSE      FALSE          FALSE
2              FALSE             FALSE      FALSE      FALSE          FALSE
  adj_tobit_residual lambda.z.n.points_blq thalf.eff.last thalf.eff.iv.last
1              FALSE                 FALSE          FALSE             FALSE
2              FALSE                 FALSE          FALSE             FALSE
  kel.last kel.iv.last cl.sparse.last mrt.sparse.last vss.sparse.last
1    FALSE       FALSE          FALSE           FALSE           FALSE
2    FALSE       FALSE          FALSE           FALSE           FALSE
  aucinf.obs aucinf.pred aumcinf.obs aumcinf.pred aucint.inf.obs
1      FALSE       FALSE       FALSE        FALSE          FALSE
2       TRUE       FALSE       FALSE        FALSE          FALSE
  aucint.inf.obs.dose aucint.inf.pred aucint.inf.pred.dose aucivinf.obs
1               FALSE           FALSE                FALSE        FALSE
2               FALSE           FALSE                FALSE        FALSE
  aucivinf.pred aucivpbextinf.obs aucivpbextinf.pred aucpext.obs aucpext.pred
1         FALSE             FALSE              FALSE       FALSE        FALSE
2         FALSE             FALSE              FALSE       FALSE        FALSE
  kel.sparse.last cl.obs cl.pred mrt.obs mrt.pred mrt.iv.obs mrt.iv.pred
1           FALSE  FALSE   FALSE   FALSE    FALSE      FALSE       FALSE
2           FALSE  FALSE   FALSE   FALSE    FALSE      FALSE       FALSE
  mrt.md.obs mrt.md.pred vz.obs vz.pred vz.sparse.last vss.obs vss.pred
1      FALSE       FALSE  FALSE   FALSE          FALSE   FALSE    FALSE
2      FALSE       FALSE  FALSE   FALSE          FALSE   FALSE    FALSE
  vss.iv.obs vss.iv.pred vss.md.obs vss.md.pred cav.int.inf.obs
1      FALSE       FALSE      FALSE       FALSE           FALSE
2      FALSE       FALSE      FALSE       FALSE           FALSE
  cav.int.inf.pred thalf.eff.obs thalf.eff.pred thalf.eff.iv.obs
1            FALSE         FALSE          FALSE            FALSE
2            FALSE         FALSE          FALSE            FALSE
  thalf.eff.iv.pred kel.obs kel.pred kel.iv.obs kel.iv.pred auclast.dn
1             FALSE   FALSE    FALSE      FALSE       FALSE      FALSE
2             FALSE   FALSE    FALSE      FALSE       FALSE      FALSE
  aucall.dn aucinf.obs.dn aucinf.pred.dn aumclast.dn aumcall.dn aumcinf.obs.dn
1     FALSE         FALSE          FALSE       FALSE      FALSE          FALSE
2     FALSE         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
1       FALSE      FALSE       FALSE
2       FALSE      FALSE       FALSE

$allow_partial_missing_units
[1] FALSE

$hl_method
[1] "log-linear"

$tobit_n_points_penalty
[1] 0

$tobit_optim_control
list()

Note: PKNCA.options.describe() is exported but non-functional in PKNCA 0.12.1 (raises an error). Use PKNCA.options() to inspect current settings, and see the Workflow Overview page for the full option reference table.


18.12 Choosing an option value: PKNCA.choose.option()

Selects a value from either the local options list passed to PKNCAdata() or the global PKNCA.options() defaults. Useful when writing custom parameter functions that need to respect session-level settings.

# With no local override, returns the global default
PKNCA.choose.option("min.hl.points", options = list())
[1] 3
# With a local override, the local value wins
PKNCA.choose.option("min.hl.points", options = list(min.hl.points = 5))
[1] 5