21  NCA Parameter Dependencies

21.1 Overview

PKNCA resolves parameter dependencies automatically. When you request aucinf.obs, PKNCA internally ensures lambda.z is computed first because aucinf.obs depends on it. This graph makes those dependencies explicit.

21.2 How dependencies are defined

Every NCA parameter is registered via add.interval.col() with a depends vector listing the parameters that must be computed before it. The full registry is accessible via get.interval.cols().

21.3 Interactive dependency graph

# Get all registered interval columns (parameters)
cols <- get.interval.cols()

# Build edge list from the 'depends' field
edges <- lapply(names(cols), function(param) {
  deps <- cols[[param]]$depends
  if (length(deps) == 0) return(NULL)
  data.frame(from = deps, to = param, stringsAsFactors = FALSE)
}) |>
  bind_rows()

# All unique nodes
all_nodes <- unique(c(edges$from, edges$to, names(cols)))

# Assign category for colouring
categorise <- function(name) {
  case_when(
    grepl("^auc",       name) ~ "AUC",
    grepl("^lambda|half\\.life|r\\.squared", name) ~ "Half-life / λz",
    grepl("^cl\\.|^vz\\.|^vss", name) ~ "CL / Volume",
    grepl("^mrt",       name) ~ "MRT",
    grepl("^c(max|min|0|last|first)|^t(max|min|last|first|lag)|clast", name) ~ "Cmax / Tmax",
    grepl("^f$|bioavail|^ae|^fe|^clr|^vd",  name) ~ "Bioavailability / Renal",
    grepl("^tss",       name) ~ "Steady-state",
    TRUE ~ "Other"
  )
}

category_colours <- c(
  "AUC"                    = "#4e79a7",
  "Half-life / λz"         = "#f28e2b",
  "CL / Volume"            = "#e15759",
  "MRT"                    = "#76b7b2",
  "Cmax / Tmax"            = "#59a14f",
  "Bioavailability / Renal"= "#edc948",
  "Steady-state"           = "#b07aa1",
  "Other"                  = "#aaaaaa"
)

nodes <- data.frame(
  id    = all_nodes,
  label = all_nodes,
  group = categorise(all_nodes),
  stringsAsFactors = FALSE
) |>
  mutate(color = category_colours[group])

visNetwork(nodes, edges,
           width = "100%", height = "700px",
           main = "PKNCA NCA Parameter Dependencies") |>
  visNodes(shape = "dot", size = 14,
           font = list(size = 12),
           borderWidth = 2) |>
  visEdges(arrows = "to",
           color  = list(color = "#aaaaaa", highlight = "#333333"),
           smooth = list(type = "cubicBezier")) |>
  visOptions(
    highlightNearest = list(enabled = TRUE, degree = 2, hover = TRUE),
    nodesIdSelection = list(enabled = TRUE, main = "Select parameter")
  ) |>
  visLegend(useGroups = TRUE, position = "right") |>
  visPhysics(stabilization = list(iterations = 200)) |>
  visLayout(randomSeed = 42)

How to use this graph:

  • Click a parameter node to highlight its immediate dependencies and dependents
  • The “Select parameter” dropdown jumps to a specific node
  • Drag nodes to rearrange; scroll to zoom
  • Colours indicate parameter family (see legend)

21.4 Dependency table

For a text-based reference:

dep_table <- lapply(names(cols), function(param) {
  data.frame(
    parameter = param,
    depends_on = if (length(cols[[param]]$depends) == 0) "(none)"
                 else paste(cols[[param]]$depends, collapse = ", "),
    stringsAsFactors = FALSE
  )
}) |>
  bind_rows() |>
  arrange(parameter)

knitr::kable(dep_table, col.names = c("Parameter", "Depends on"))
Parameter Depends on
adj.r.squared half.life
adj_tobit_residual half.life
ae (none)
aucabove.predose.all cstart
aucabove.trough.all ctrough
aucall (none)
aucall.dn aucall
aucinf.obs lambda.z, clast.obs
aucinf.obs.dn aucinf.obs
aucinf.pred lambda.z, clast.pred
aucinf.pred.dn aucinf.pred
aucint.all (none)
aucint.all.dose (none)
aucint.inf.obs lambda.z, clast.obs
aucint.inf.obs.dose lambda.z, clast.obs
aucint.inf.pred lambda.z, clast.pred
aucint.inf.pred.dose lambda.z, clast.pred
aucint.last (none)
aucint.last.dose (none)
aucivall aucall, c0
aucivinf.obs aucinf.obs, c0
aucivinf.pred aucinf.pred, c0
aucivint.all aucint.all, c0
aucivint.last aucint.last, c0
aucivlast auclast, c0
aucivpbextall aucall, aucivall
aucivpbextinf.obs aucinf.obs, aucivinf.obs
aucivpbextinf.pred aucinf.pred, aucivinf.pred
aucivpbextint.all aucint.all, aucivint.all
aucivpbextint.last aucint.last, aucivint.last
aucivpbextlast auclast, aucivlast
auclast (none)
auclast.dn auclast
aucpext.obs auclast, aucinf.obs
aucpext.pred auclast, aucinf.pred
aumcall (none)
aumcall.dn aumcall
aumcinf.obs lambda.z, clast.obs
aumcinf.obs.dn aumcinf.obs
aumcinf.pred lambda.z, clast.pred
aumcinf.pred.dn aumcinf.pred
aumclast (none)
aumclast.dn aumclast
c0 (none)
cav auclast
cav.dn cav
cav.int.all aucint.all
cav.int.inf.obs aucint.inf.obs
cav.int.inf.pred aucint.inf.pred
cav.int.last aucint.last
ceoi (none)
cl.all aucall
cl.last auclast
cl.obs aucinf.obs
cl.pred aucinf.pred
cl.sparse.last sparse_auclast
clast.obs (none)
clast.obs.dn clast.obs
clast.pred half.life
clast.pred.dn clast.pred
clr.last ae
clr.last.dn clr.last
clr.obs ae
clr.obs.dn clr.obs
clr.pred ae
clr.pred.dn clr.pred
cmax (none)
cmax.dn cmax
cmin (none)
cmin.dn cmin
count_conc (none)
count_conc_measured (none)
cstart (none)
ctrough (none)
ctrough.dn ctrough
deg.fluc cmax, cmin, cav
end (none)
ermax (none)
ertlst (none)
ertmax (none)
f (none)
fe ae
half.life tmax, tlast
kel.iv.last mrt.iv.last
kel.iv.obs mrt.iv.obs
kel.iv.pred mrt.iv.pred
kel.last mrt.last
kel.obs mrt.obs
kel.pred mrt.pred
kel.sparse.last mrt.sparse.last
lambda.z half.life
lambda.z.corrxy half.life
lambda.z.n.points half.life
lambda.z.n.points_blq half.life
lambda.z.time.first half.life
lambda.z.time.last half.life
mrt.iv.last auclast, aumclast
mrt.iv.obs aucinf.obs, aumcinf.obs
mrt.iv.pred aucinf.pred, aumcinf.pred
mrt.last auclast, aumclast
mrt.md.obs auclast, aumclast, aucinf.obs
mrt.md.pred auclast, aumclast, aucinf.pred
mrt.obs aucinf.obs, aumcinf.obs
mrt.pred aucinf.pred, aumcinf.pred
mrt.sparse.last sparse_auclast, sparse_aumclast
ptr cmax, ctrough
r.squared half.life
span.ratio half.life
sparse_auc_df sparse_auclast
sparse_auc_se sparse_auclast
sparse_auclast (none)
sparse_aumc_df sparse_aumclast
sparse_aumc_se sparse_aumclast
sparse_aumclast sparse_auclast
start (none)
swing cmax, cmin
tfirst (none)
thalf.eff.iv.last mrt.iv.last
thalf.eff.iv.obs mrt.iv.obs
thalf.eff.iv.pred mrt.iv.pred
thalf.eff.last mrt.last
thalf.eff.obs mrt.obs
thalf.eff.pred mrt.pred
time_above (none)
tlag (none)
tlast (none)
tmax (none)
tmin (none)
tobit_residual half.life
totdose (none)
volpk (none)
vss.iv.last cl.last, mrt.iv.last
vss.iv.obs cl.obs, mrt.iv.obs
vss.iv.pred cl.pred, mrt.iv.pred
vss.last cl.last, mrt.last
vss.md.obs cl.last, mrt.md.obs
vss.md.pred cl.last, mrt.md.pred
vss.obs cl.obs, mrt.obs
vss.pred cl.pred, mrt.pred
vss.sparse.last cl.sparse.last, mrt.sparse.last
vz.obs cl.obs, lambda.z
vz.pred cl.pred, lambda.z
vz.sparse.last cl.sparse.last, kel.sparse.last

21.5 Dependency resolution

When you call pk.nca(), PKNCA calls sort.interval.cols() internally to produce a topologically sorted execution order — parameters with no dependencies run first, then those that depend on already-computed values.

# Reconstruct execution order: parameters with no dependencies first,
# then those whose dependencies are already satisfied.
cols <- get.interval.cols()
dep_counts <- sapply(cols, function(x) length(x$depends))
all_deps <- data.frame(
  parameter  = names(dep_counts),
  n_depends  = unname(dep_counts)
) |>
  arrange(n_depends, parameter)

cat(sprintf("%d parameters have no dependencies (base parameters).\n", sum(all_deps$n_depends == 0)))
33 parameters have no dependencies (base parameters).
cat(sprintf("%d parameters have at least one dependency (derived parameters).\n\n", sum(all_deps$n_depends > 0)))
110 parameters have at least one dependency (derived parameters).
# Show only derived parameters — these illustrate the resolution order
all_deps |>
  filter(n_depends > 0) |>
  knitr::kable(
    col.names = c("Parameter", "N dependencies"),
    caption   = "Derived parameters ordered by number of dependencies (execution order)"
  )
Derived parameters ordered by number of dependencies (execution order)
Parameter N dependencies
adj.r.squared 1
adj_tobit_residual 1
aucabove.predose.all 1
aucabove.trough.all 1
aucall.dn 1
aucinf.obs.dn 1
aucinf.pred.dn 1
auclast.dn 1
aumcall.dn 1
aumcinf.obs.dn 1
aumcinf.pred.dn 1
aumclast.dn 1
cav 1
cav.dn 1
cav.int.all 1
cav.int.inf.obs 1
cav.int.inf.pred 1
cav.int.last 1
cl.all 1
cl.last 1
cl.obs 1
cl.pred 1
cl.sparse.last 1
clast.obs.dn 1
clast.pred 1
clast.pred.dn 1
clr.last 1
clr.last.dn 1
clr.obs 1
clr.obs.dn 1
clr.pred 1
clr.pred.dn 1
cmax.dn 1
cmin.dn 1
ctrough.dn 1
fe 1
kel.iv.last 1
kel.iv.obs 1
kel.iv.pred 1
kel.last 1
kel.obs 1
kel.pred 1
kel.sparse.last 1
lambda.z 1
lambda.z.corrxy 1
lambda.z.n.points 1
lambda.z.n.points_blq 1
lambda.z.time.first 1
lambda.z.time.last 1
r.squared 1
span.ratio 1
sparse_auc_df 1
sparse_auc_se 1
sparse_aumc_df 1
sparse_aumc_se 1
sparse_aumclast 1
thalf.eff.iv.last 1
thalf.eff.iv.obs 1
thalf.eff.iv.pred 1
thalf.eff.last 1
thalf.eff.obs 1
thalf.eff.pred 1
tobit_residual 1
aucinf.obs 2
aucinf.pred 2
aucint.inf.obs 2
aucint.inf.obs.dose 2
aucint.inf.pred 2
aucint.inf.pred.dose 2
aucivall 2
aucivinf.obs 2
aucivinf.pred 2
aucivint.all 2
aucivint.last 2
aucivlast 2
aucivpbextall 2
aucivpbextinf.obs 2
aucivpbextinf.pred 2
aucivpbextint.all 2
aucivpbextint.last 2
aucivpbextlast 2
aucpext.obs 2
aucpext.pred 2
aumcinf.obs 2
aumcinf.pred 2
half.life 2
mrt.iv.last 2
mrt.iv.obs 2
mrt.iv.pred 2
mrt.last 2
mrt.obs 2
mrt.pred 2
mrt.sparse.last 2
ptr 2
swing 2
vss.iv.last 2
vss.iv.obs 2
vss.iv.pred 2
vss.last 2
vss.md.obs 2
vss.md.pred 2
vss.obs 2
vss.pred 2
vss.sparse.last 2
vz.obs 2
vz.pred 2
vz.sparse.last 2
deg.fluc 3
mrt.md.obs 3
mrt.md.pred 3

pkgdown reference: get.interval.cols() · add.interval.col()