# 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)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
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 |
| aumcint.all | (none) |
| aumcint.all.dose | (none) |
| aumcint.inf.obs | lambda.z, clast.obs |
| aumcint.inf.obs.dose | lambda.z, clast.obs |
| aumcint.inf.pred | lambda.z, clast.pred |
| aumcint.inf.pred.dose | lambda.z, clast.pred |
| aumcint.last | (none) |
| aumcint.last.dose | (none) |
| aumcivall | aumcall, c0 |
| aumcivinf.obs | aumcinf.obs, c0 |
| aumcivinf.pred | aumcinf.pred, c0 |
| aumcivint.all | aumcint.all, c0 |
| aumcivint.last | aumcint.last, c0 |
| aumcivlast | aumclast, c0 |
| 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.int.all | aucint.all |
| cl.int.inf.obs | aucint.inf.obs |
| cl.int.inf.pred | aucint.inf.pred |
| cl.int.last | aucint.last |
| cl.iv.all | aucivall |
| cl.iv.last | aucivlast |
| cl.iv.obs | aucivinf.obs |
| cl.iv.pred | aucivinf.pred |
| cl.ivint.all | aucivint.all |
| cl.ivint.last | aucivint.last |
| 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.all | mrt.all |
| kel.int.all | mrt.int.all |
| kel.int.inf.obs | mrt.int.inf.obs |
| kel.int.inf.pred | mrt.int.inf.pred |
| kel.int.last | mrt.int.last |
| kel.iv.all | mrt.iv.all |
| kel.iv.last | mrt.iv.last |
| kel.iv.obs | mrt.iv.obs |
| kel.iv.pred | mrt.iv.pred |
| kel.ivint.all | mrt.ivint.all |
| kel.ivint.last | mrt.ivint.last |
| 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.all | aucall, aumcall |
| mrt.int.all | aucint.all, aumcint.all |
| mrt.int.inf.obs | aucint.inf.obs, aumcint.inf.obs |
| mrt.int.inf.pred | aucint.inf.pred, aumcint.inf.pred |
| mrt.int.last | aucint.last, aumcint.last |
| mrt.iv.all | aucivall, aumcivall |
| mrt.iv.last | auclast, aumclast |
| mrt.iv.obs | aucinf.obs, aumcinf.obs |
| mrt.iv.pred | aucinf.pred, aumcinf.pred |
| mrt.ivint.all | aucivint.all, aumcivint.all |
| mrt.ivint.last | aucivint.last, aumcivint.last |
| 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.all | cl.all, mrt.all |
| vss.int.all | cl.int.all, mrt.int.all |
| vss.int.inf.obs | cl.int.inf.obs, mrt.int.inf.obs |
| vss.int.inf.pred | cl.int.inf.pred, mrt.int.inf.pred |
| vss.int.last | cl.int.last, mrt.int.last |
| vss.iv.all | cl.iv.all, mrt.iv.all |
| 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.ivint.all | cl.ivint.all, mrt.ivint.all |
| vss.ivint.last | cl.ivint.last, mrt.ivint.last |
| 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.all | cl.all, lambda.z |
| vz.int.all | cl.int.all, lambda.z |
| vz.int.inf.obs | cl.int.inf.obs, lambda.z |
| vz.int.inf.pred | cl.int.inf.pred, lambda.z |
| vz.int.last | cl.int.last, lambda.z |
| vz.iv.all | cl.iv.all, lambda.z |
| vz.iv.last | cl.iv.last, lambda.z |
| vz.iv.obs | cl.iv.obs, lambda.z |
| vz.iv.pred | cl.iv.pred, lambda.z |
| vz.ivint.all | cl.ivint.all, lambda.z |
| vz.ivint.last | cl.ivint.last, lambda.z |
| vz.last | cl.last, lambda.z |
| 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)))37 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)))166 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)"
)| 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.int.all | 1 |
| cl.int.inf.obs | 1 |
| cl.int.inf.pred | 1 |
| cl.int.last | 1 |
| cl.iv.all | 1 |
| cl.iv.last | 1 |
| cl.iv.obs | 1 |
| cl.iv.pred | 1 |
| cl.ivint.all | 1 |
| cl.ivint.last | 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.all | 1 |
| kel.int.all | 1 |
| kel.int.inf.obs | 1 |
| kel.int.inf.pred | 1 |
| kel.int.last | 1 |
| kel.iv.all | 1 |
| kel.iv.last | 1 |
| kel.iv.obs | 1 |
| kel.iv.pred | 1 |
| kel.ivint.all | 1 |
| kel.ivint.last | 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 |
| aumcint.inf.obs | 2 |
| aumcint.inf.obs.dose | 2 |
| aumcint.inf.pred | 2 |
| aumcint.inf.pred.dose | 2 |
| aumcivall | 2 |
| aumcivinf.obs | 2 |
| aumcivinf.pred | 2 |
| aumcivint.all | 2 |
| aumcivint.last | 2 |
| aumcivlast | 2 |
| half.life | 2 |
| mrt.all | 2 |
| mrt.int.all | 2 |
| mrt.int.inf.obs | 2 |
| mrt.int.inf.pred | 2 |
| mrt.int.last | 2 |
| mrt.iv.all | 2 |
| mrt.iv.last | 2 |
| mrt.iv.obs | 2 |
| mrt.iv.pred | 2 |
| mrt.ivint.all | 2 |
| mrt.ivint.last | 2 |
| mrt.last | 2 |
| mrt.obs | 2 |
| mrt.pred | 2 |
| mrt.sparse.last | 2 |
| ptr | 2 |
| swing | 2 |
| vss.all | 2 |
| vss.int.all | 2 |
| vss.int.inf.obs | 2 |
| vss.int.inf.pred | 2 |
| vss.int.last | 2 |
| vss.iv.all | 2 |
| vss.iv.last | 2 |
| vss.iv.obs | 2 |
| vss.iv.pred | 2 |
| vss.ivint.all | 2 |
| vss.ivint.last | 2 |
| vss.last | 2 |
| vss.md.obs | 2 |
| vss.md.pred | 2 |
| vss.obs | 2 |
| vss.pred | 2 |
| vss.sparse.last | 2 |
| vz.all | 2 |
| vz.int.all | 2 |
| vz.int.inf.obs | 2 |
| vz.int.inf.pred | 2 |
| vz.int.last | 2 |
| vz.iv.all | 2 |
| vz.iv.last | 2 |
| vz.iv.obs | 2 |
| vz.iv.pred | 2 |
| vz.ivint.all | 2 |
| vz.ivint.last | 2 |
| vz.last | 2 |
| vz.obs | 2 |
| vz.pred | 2 |
| vz.sparse.last | 2 |
| deg.fluc | 3 |
| mrt.md.obs | 3 |
| mrt.md.pred | 3 |