# 123 parameters registered by default
length(get.interval.cols())[1] 203
add.interval.col()
formalsmap argumentEvery NCA parameter that PKNCA can compute is registered in an internal registry via add.interval.col(). When you request auclast = TRUE in an interval, PKNCA looks up the registered function for auclast and calls it with the right arguments.
You can extend the registry with your own parameters — new functions that compute anything derivable from concentrations, times, doses, or other already-computed parameters.
add.interval.col()add.interval.col(
name = "my.param", # column name used in intervals data frame
FUN = "pk.calc.my.param", # character string: function name
values = c(FALSE, TRUE), # allowed values in intervals (FALSE = skip, TRUE = compute)
unit_type = "time", # units category (see below)
pretty_name = "My parameter", # human-readable label
desc = "What it does", # one-line description
depends = NULL, # other parameters that must be computed first
sparse = FALSE, # TRUE if this is a sparse-PK parameter
formalsmap = list(), # how to map registered arg names to your function args
datatype = "interval" # "interval" (default), "individual", or "population"
)Key rule: FUN must be a character string (the function name), not a function object.
unit_type values"time", "conc", "auc", "aumc", "clearance", "volume", "fraction", "dose", "amount", "%", "count", "unitless", "inverse_time", "renal_clearance", "auc_dosenorm", "conc_dosenorm", "aumc_dosenorm", "amount_dose"
A parameter that divides two already-computed parameters. Use depends to declare what must be computed first, and formalsmap to map PKNCA’s internal argument names to your function’s parameter names.
# Exposure ratio: AUClast / Cmax (units: time, since AUC/conc = time × conc/conc = time)
pk.calc.exposure.ratio <- function(auclast, cmax) {
auclast / cmax
}
add.interval.col(
name = "exposure.ratio",
FUN = "pk.calc.exposure.ratio",
values = c(FALSE, TRUE),
unit_type = "time",
pretty_name = "Exposure ratio (AUClast / Cmax)",
desc = "AUClast divided by Cmax",
depends = c("auclast", "cmax"),
formalsmap = list(auclast = "auclast", cmax = "cmax")
)o_nca_er <- pk.nca(PKNCAdata(o_conc, o_dose, intervals = data.frame(
start = 0, end = Inf,
auclast = TRUE,
cmax = TRUE,
exposure.ratio = TRUE
)))
as.data.frame(o_nca_er) |>
filter(PPTESTCD %in% c("auclast", "cmax", "exposure.ratio")) |>
select(subject, PPTESTCD, PPORRES) |>
tidyr::pivot_wider(names_from = PPTESTCD, values_from = PPORRES) |>
mutate(check = auclast / cmax) |>
arrange(subject)# A tibble: 12 × 5
subject auclast cmax exposure.ratio check
<ord> <dbl> <dbl> <dbl> <dbl>
1 6 71.7 6.44 11.1 11.1
2 7 88.0 7.09 12.4 12.4
3 8 86.8 7.56 11.5 11.5
4 11 77.9 8 9.74 9.74
5 3 95.9 8.2 11.7 11.7
6 2 88.7 8.33 10.7 10.7
7 4 103. 8.6 11.9 11.9
8 9 83.9 9.03 9.30 9.30
9 12 115. 9.75 11.8 11.8
10 10 136. 10.2 13.3 13.3
11 1 147. 10.5 14.0 14.0
12 5 118. 11.4 10.4 10.4
A parameter that accesses conc and time directly. PKNCA passes the raw vectors from the concentration data. No depends needed — just use conc and time as argument names and map them in formalsmap.
# Concentration at exactly 2 hours (interpolated)
pk.calc.c_at_2h <- function(conc, time) {
interp.extrap.conc(conc = conc, time = time, time.out = 2)
}
add.interval.col(
name = "c.2h",
FUN = "pk.calc.c_at_2h",
values = c(FALSE, TRUE),
unit_type = "conc",
pretty_name = "Concentration at 2 h",
desc = "Interpolated concentration at t = 2 h",
depends = NULL,
formalsmap = list(conc = "conc", time = "time")
)# A tibble: 12 × 2
subject PPORRES
<ord> <dbl>
1 6 6.32
2 7 6.55
3 8 7.56
4 11 6.80
5 3 7.81
6 2 8.25
7 4 8.41
8 9 6.35
9 12 9.72
10 10 7.76
11 1 9.68
12 5 9.37
When computing several related quantities (e.g., early AUC, late AUC, and their ratio), register each as its own function. Each function reads from the already-computed auclast dependency.
pk.calc.auc.early <- function(conc, time) {
t_idx <- time <= 4
if (sum(t_idx) < 2) return(NA_real_)
pk.calc.auc.last(conc[t_idx], time[t_idx])
}
pk.calc.auc.late <- function(conc, time, auclast) {
t_idx <- time <= 4
if (sum(t_idx) < 2) return(NA_real_)
auclast - pk.calc.auc.last(conc[t_idx], time[t_idx])
}
pk.calc.auc.early.frac <- function(conc, time, auclast) {
t_idx <- time <= 4
if (sum(t_idx) < 2) return(NA_real_)
pk.calc.auc.last(conc[t_idx], time[t_idx]) / auclast
}
add.interval.col(
name = "auc.early",
FUN = "pk.calc.auc.early",
values = c(FALSE, TRUE),
unit_type = "auc",
pretty_name = "AUC 0-4h",
desc = "AUC from 0 to 4 h",
depends = NULL,
formalsmap = list(conc = "conc", time = "time")
)
add.interval.col(
name = "auc.late",
FUN = "pk.calc.auc.late",
values = c(FALSE, TRUE),
unit_type = "auc",
pretty_name = "AUC 4h to last",
desc = "AUC from 4 h to last sample",
depends = "auclast",
formalsmap = list(conc = "conc", time = "time", auclast = "auclast")
)
add.interval.col(
name = "auc.early.fraction",
FUN = "pk.calc.auc.early.frac",
values = c(FALSE, TRUE),
unit_type = "fraction",
pretty_name = "Early AUC fraction",
desc = "Fraction of AUClast occurring in first 4 h",
depends = "auclast",
formalsmap = list(conc = "conc", time = "time", auclast = "auclast")
)Note: Each parameter must be registered with its own distinct function. PKNCA does not support returning a named list from a single function to populate multiple output parameters — each output needs its own
add.interval.col()call.
o_nca_split <- pk.nca(PKNCAdata(o_conc, o_dose, intervals = data.frame(
start = 0, end = Inf,
auclast = TRUE,
auc.early = TRUE,
auc.late = TRUE,
auc.early.fraction = TRUE
)))
as.data.frame(o_nca_split) |>
filter(PPTESTCD %in% c("auclast", "auc.early", "auc.late", "auc.early.fraction")) |>
select(subject, PPTESTCD, PPORRES) |>
tidyr::pivot_wider(names_from = PPTESTCD, values_from = PPORRES) |>
arrange(subject)# A tibble: 12 × 5
subject auclast auc.early auc.late auc.early.fraction
<ord> <dbl> <dbl> <dbl> <dbl>
1 6 71.7 18.3 53.4 0.255
2 7 88.0 18.2 69.7 0.207
3 8 86.8 22.0 64.8 0.253
4 11 77.9 23.4 54.5 0.301
5 3 95.9 25.9 70.0 0.270
6 2 88.7 24.9 63.8 0.281
7 4 103. 24.1 78.5 0.235
8 9 83.9 22.8 61.2 0.271
9 12 115. 27.3 87.9 0.237
10 10 136. 24.5 111. 0.181
11 1 147. 32.1 115. 0.218
12 5 118. 29.1 89.0 0.247
formalsmap argumentformalsmap maps PKNCA’s standard argument names to your function’s parameter names. PKNCA always passes data using its own internal names; formalsmap tells it which of those names maps to which argument in your function.
Standard PKNCA argument names available via formalsmap:
| PKNCA name | What it provides |
|---|---|
conc |
Concentration vector for the current interval |
time |
Time vector for the current interval |
dose |
Total dose in the current interval |
start |
Interval start time |
end |
Interval end time |
half.life |
Computed half-life (if in depends) |
auclast |
Computed AUClast (if in depends) |
cmax |
Computed Cmax (if in depends) |
| (any parameter name) | Any other registered parameter in depends |
If your function argument names exactly match PKNCA’s standard names, formalsmap can be omitted or left empty.
exposure.ratio registered: TRUE
c.2h registered: TRUE
List of 9
$ FUN : chr "pk.calc.exposure.ratio"
$ values : logi [1:2] FALSE TRUE
$ unit_type : chr "time"
$ pretty_name: chr "Exposure ratio (AUClast / Cmax)"
$ desc : chr "AUClast divided by Cmax"
$ sparse : logi FALSE
$ formalsmap :List of 2
..$ auclast: chr "auclast"
..$ cmax : chr "cmax"
$ depends : chr [1:2] "auclast" "cmax"
$ datatype : chr "interval"
After registering the parameter, set a custom summary rule with PKNCA.set.summary():
start end N auclast cmax exposure.ratio
0 Inf 12 98.7 [22.5] 8.65 [17.0] 11.4 [12.0]
Caption: auclast, cmax: geometric mean and geometric coefficient of variation; exposure.ratio: geometric mean [CV%]; N: number of subjects
add.interval.col() persist for the life of your R session. Re-run registration code in each session (e.g., in your analysis script or package).FUN must be a character string. Passing a function object causes an error.depends controls evaluation order. List all parameters your function reads. PKNCA resolves the dependency graph and ensures they are computed before your function is called.pk.nca() runs. Define them in the global environment or in a package loaded before calling pk.nca().pkgdown reference: PKNCAconc() · PKNCAdose() · PKNCAdata() · pk.nca() · add.interval.col() · get.interval.cols() · PKNCA.set.summary()
---
title: "Writing Custom Parameter Functions"
---
```{r setup, include=FALSE}
library(PKNCA)
library(dplyr)
library(ggplot2)
conflicted::conflicts_prefer(dplyr::filter, dplyr::select, .quiet = TRUE)
# Shared dataset
d_conc <- as.data.frame(Theoph) |> rename(time = Time, subject = Subject)
d_dose <- Theoph |> as.data.frame() |>
group_by(Subject) |>
summarise(dose = Dose[1] * Wt[1], .groups = "drop") |>
rename(subject = Subject) |>
mutate(time = 0)
o_conc <- PKNCAconc(d_conc, conc ~ time | subject)
o_dose <- PKNCAdose(d_dose, dose ~ time | subject, route = "extravascular")
```
## The parameter registry
Every NCA parameter that PKNCA can compute is **registered** in an internal registry via `add.interval.col()`. When you request `auclast = TRUE` in an interval, PKNCA looks up the registered function for `auclast` and calls it with the right arguments.
You can extend the registry with your own parameters — new functions that compute anything derivable from concentrations, times, doses, or other already-computed parameters.
```{r}
# 123 parameters registered by default
length(get.interval.cols())
```
---
## Anatomy of `add.interval.col()`
```r
add.interval.col(
name = "my.param", # column name used in intervals data frame
FUN = "pk.calc.my.param", # character string: function name
values = c(FALSE, TRUE), # allowed values in intervals (FALSE = skip, TRUE = compute)
unit_type = "time", # units category (see below)
pretty_name = "My parameter", # human-readable label
desc = "What it does", # one-line description
depends = NULL, # other parameters that must be computed first
sparse = FALSE, # TRUE if this is a sparse-PK parameter
formalsmap = list(), # how to map registered arg names to your function args
datatype = "interval" # "interval" (default), "individual", or "population"
)
```
**Key rule:** `FUN` must be a **character string** (the function name), not a function object.
### Available `unit_type` values
`"time"`, `"conc"`, `"auc"`, `"aumc"`, `"clearance"`, `"volume"`, `"fraction"`, `"dose"`, `"amount"`, `"%"`, `"count"`, `"unitless"`, `"inverse_time"`, `"renal_clearance"`, `"auc_dosenorm"`, `"conc_dosenorm"`, `"aumc_dosenorm"`, `"amount_dose"`
---
## Example 1: derived from other parameters
A parameter that divides two already-computed parameters. Use `depends` to declare what must be computed first, and `formalsmap` to map PKNCA's internal argument names to your function's parameter names.
```{r}
# Exposure ratio: AUClast / Cmax (units: time, since AUC/conc = time × conc/conc = time)
pk.calc.exposure.ratio <- function(auclast, cmax) {
auclast / cmax
}
add.interval.col(
name = "exposure.ratio",
FUN = "pk.calc.exposure.ratio",
values = c(FALSE, TRUE),
unit_type = "time",
pretty_name = "Exposure ratio (AUClast / Cmax)",
desc = "AUClast divided by Cmax",
depends = c("auclast", "cmax"),
formalsmap = list(auclast = "auclast", cmax = "cmax")
)
```
```{r}
o_nca_er <- pk.nca(PKNCAdata(o_conc, o_dose, intervals = data.frame(
start = 0, end = Inf,
auclast = TRUE,
cmax = TRUE,
exposure.ratio = TRUE
)))
as.data.frame(o_nca_er) |>
filter(PPTESTCD %in% c("auclast", "cmax", "exposure.ratio")) |>
select(subject, PPTESTCD, PPORRES) |>
tidyr::pivot_wider(names_from = PPTESTCD, values_from = PPORRES) |>
mutate(check = auclast / cmax) |>
arrange(subject)
```
---
## Example 2: computed from raw concentration-time data
A parameter that accesses `conc` and `time` directly. PKNCA passes the raw vectors from the concentration data. No `depends` needed — just use `conc` and `time` as argument names and map them in `formalsmap`.
```{r}
# Concentration at exactly 2 hours (interpolated)
pk.calc.c_at_2h <- function(conc, time) {
interp.extrap.conc(conc = conc, time = time, time.out = 2)
}
add.interval.col(
name = "c.2h",
FUN = "pk.calc.c_at_2h",
values = c(FALSE, TRUE),
unit_type = "conc",
pretty_name = "Concentration at 2 h",
desc = "Interpolated concentration at t = 2 h",
depends = NULL,
formalsmap = list(conc = "conc", time = "time")
)
```
```{r}
o_nca_c2 <- pk.nca(PKNCAdata(o_conc, o_dose, intervals = data.frame(
start = 0, end = Inf, c.2h = TRUE
)))
as.data.frame(o_nca_c2) |>
filter(PPTESTCD == "c.2h") |>
select(subject, PPORRES) |>
arrange(subject)
```
---
## Example 3: multiple related parameters sharing a dependency
When computing several related quantities (e.g., early AUC, late AUC, and their ratio), register each as its own function. Each function reads from the already-computed `auclast` dependency.
```{r}
pk.calc.auc.early <- function(conc, time) {
t_idx <- time <= 4
if (sum(t_idx) < 2) return(NA_real_)
pk.calc.auc.last(conc[t_idx], time[t_idx])
}
pk.calc.auc.late <- function(conc, time, auclast) {
t_idx <- time <= 4
if (sum(t_idx) < 2) return(NA_real_)
auclast - pk.calc.auc.last(conc[t_idx], time[t_idx])
}
pk.calc.auc.early.frac <- function(conc, time, auclast) {
t_idx <- time <= 4
if (sum(t_idx) < 2) return(NA_real_)
pk.calc.auc.last(conc[t_idx], time[t_idx]) / auclast
}
add.interval.col(
name = "auc.early",
FUN = "pk.calc.auc.early",
values = c(FALSE, TRUE),
unit_type = "auc",
pretty_name = "AUC 0-4h",
desc = "AUC from 0 to 4 h",
depends = NULL,
formalsmap = list(conc = "conc", time = "time")
)
add.interval.col(
name = "auc.late",
FUN = "pk.calc.auc.late",
values = c(FALSE, TRUE),
unit_type = "auc",
pretty_name = "AUC 4h to last",
desc = "AUC from 4 h to last sample",
depends = "auclast",
formalsmap = list(conc = "conc", time = "time", auclast = "auclast")
)
add.interval.col(
name = "auc.early.fraction",
FUN = "pk.calc.auc.early.frac",
values = c(FALSE, TRUE),
unit_type = "fraction",
pretty_name = "Early AUC fraction",
desc = "Fraction of AUClast occurring in first 4 h",
depends = "auclast",
formalsmap = list(conc = "conc", time = "time", auclast = "auclast")
)
```
> **Note:** Each parameter must be registered with its own distinct function. PKNCA does not support returning a named list from a single function to populate multiple output parameters — each output needs its own `add.interval.col()` call.
```{r}
o_nca_split <- pk.nca(PKNCAdata(o_conc, o_dose, intervals = data.frame(
start = 0, end = Inf,
auclast = TRUE,
auc.early = TRUE,
auc.late = TRUE,
auc.early.fraction = TRUE
)))
as.data.frame(o_nca_split) |>
filter(PPTESTCD %in% c("auclast", "auc.early", "auc.late", "auc.early.fraction")) |>
select(subject, PPTESTCD, PPORRES) |>
tidyr::pivot_wider(names_from = PPTESTCD, values_from = PPORRES) |>
arrange(subject)
```
---
## The `formalsmap` argument
`formalsmap` maps PKNCA's standard argument names to your function's parameter names. PKNCA always passes data using its own internal names; `formalsmap` tells it which of those names maps to which argument in your function.
Standard PKNCA argument names available via `formalsmap`:
| PKNCA name | What it provides |
|---|---|
| `conc` | Concentration vector for the current interval |
| `time` | Time vector for the current interval |
| `dose` | Total dose in the current interval |
| `start` | Interval start time |
| `end` | Interval end time |
| `half.life` | Computed half-life (if in `depends`) |
| `auclast` | Computed AUClast (if in `depends`) |
| `cmax` | Computed Cmax (if in `depends`) |
| *(any parameter name)* | Any other registered parameter in `depends` |
If your function argument names **exactly match** PKNCA's standard names, `formalsmap` can be omitted or left empty.
---
## Inspecting registered parameters
```{r}
# Confirm your parameter is registered
cols <- get.interval.cols()
cat("exposure.ratio registered:", "exposure.ratio" %in% names(cols), "\n")
cat("c.2h registered: ", "c.2h" %in% names(cols), "\n")
```
```{r}
# Inspect registration details
str(cols[["exposure.ratio"]])
```
---
## Custom summary statistics for your parameter
After registering the parameter, set a custom summary rule with `PKNCA.set.summary()`:
```{r}
PKNCA.set.summary(
"exposure.ratio",
description = "geometric mean [CV%]",
point = PKNCA:::geomean,
spread = PKNCA:::geocv
)
summary(pk.nca(PKNCAdata(o_conc, o_dose, intervals = data.frame(
start = 0, end = Inf,
auclast = TRUE, cmax = TRUE, exposure.ratio = TRUE
))))
```
---
## Important notes
- **Registration is session-scoped.** Custom parameters registered with `add.interval.col()` persist for the life of your R session. Re-run registration code in each session (e.g., in your analysis script or package).
- **`FUN` must be a character string.** Passing a function object causes an error.
- **`depends` controls evaluation order.** List all parameters your function reads. PKNCA resolves the dependency graph and ensures they are computed before your function is called.
- **Functions must be in scope** when `pk.nca()` runs. Define them in the global environment or in a package loaded before calling `pk.nca()`.
---
::: {.callout-note icon=false appearance="minimal"}
**pkgdown reference:** [PKNCAconc()](https://humanpred.github.io/pknca/reference/PKNCAconc.html) · [PKNCAdose()](https://humanpred.github.io/pknca/reference/PKNCAdose.html) · [PKNCAdata()](https://humanpred.github.io/pknca/reference/PKNCAdata.html) · [pk.nca()](https://humanpred.github.io/pknca/reference/pk.nca.html) · [add.interval.col()](https://humanpred.github.io/pknca/reference/add.interval.col.html) · [get.interval.cols()](https://humanpred.github.io/pknca/reference/get.interval.cols.html) · [PKNCA.set.summary()](https://humanpred.github.io/pknca/reference/PKNCA.set.summary.html)
:::