---
title: "Extravascular (Oral/SC) Examples"
---
```{r setup, include=FALSE}
library(PKNCA)
library(dplyr)
library(ggplot2)
conflicted::conflicts_prefer(dplyr::filter, dplyr::select, .quiet = TRUE)
```
## The EV dataset: Theophylline
We use `datasets::Theoph` — oral theophylline (bronchodilator) in 12 subjects, sampled over 25 hours after a single oral dose. The package authors' worked analysis of this dataset is in the vignette [Computing NCA Parameters for Theophylline](https://humanpred.github.io/pknca/articles/v02-example-theophylline.html).
```{r}
head(Theoph)
str(Theoph)
```
Columns: `Subject`, `Wt` (body weight, kg), `Dose` (mg/kg), `Time` (h), `conc` (mg/L).
```{r}
# Prepare concentration data
d_conc <- as.data.frame(Theoph) |>
rename(time = Time, subject = Subject)
# Prepare dose data: convert mg/kg to mg per subject
d_dose <- Theoph |>
as.data.frame() |>
group_by(Subject) |>
summarise(dose = Dose[1] * Wt[1], .groups = "drop") |>
rename(subject = Subject) |>
mutate(time = 0)
# Visualize concentration profiles
ggplot(d_conc, aes(x = time, y = conc, group = subject, colour = subject)) +
geom_line() + geom_point() +
labs(title = "Theophylline oral — concentration-time profiles",
x = "Time (h)", y = "Concentration (mg/L)") +
theme_minimal()
```
The typical absorption-distribution-elimination shape is visible: concentrations rise to a peak (Tmax/Cmax) then decline.
---
## Basic extravascular analysis
```{r}
o_conc <- PKNCAconc(d_conc, conc ~ time | subject)
o_dose <- PKNCAdose(d_dose, dose ~ time | subject, route = "extravascular")
# Core EV parameters
ev_intervals <- data.frame(
start = 0,
end = Inf,
cmax = TRUE,
tmax = TRUE,
auclast = TRUE,
aucinf.obs = TRUE,
half.life = TRUE,
lambda.z = TRUE,
cl.obs = TRUE, # apparent CL (= CL/F for EV)
vz.obs = TRUE # apparent Vz (= Vz/F for EV)
)
o_data <- PKNCAdata(o_conc, o_dose, intervals = ev_intervals)
o_nca <- pk.nca(o_data)
as.data.frame(o_nca) |>
select(subject, PPTESTCD, PPORRES) |>
arrange(subject, PPTESTCD)
```
---
## Parameter reference
### Concentration and time landmarks
| Parameter | Meaning |
|---|---|
| `cmax` | Maximum observed concentration |
| `tmax` | Time of Cmax |
| `cmin` | Minimum observed concentration in the interval |
| `tmin` | Time of the minimum observed concentration (≥ 0.13.0) |
| `tfirst` | First time with a non-zero (non-BLQ) concentration |
| `tlast` | Last time with a measurable concentration |
| `clast.obs` | Observed concentration at `tlast` |
| `clast.pred` | Predicted concentration at `tlast` (from λz fit) |
| `count_conc` | Number of non-missing concentrations (including BLQ) |
| `count_conc_measured` | Number of concentrations above LOQ (non-BLQ) |
| `lambda.z.time.last` | Last timepoint used in the λz regression |
Both counts are taken after imputation and do not distinguish imputed from measured values: an imputed zero (`start_conc0`) adds to `count_conc` but not to `count_conc_measured`, while a concentration carried to the start (`start_predose`) adds to both. To count only measured samples, calculate the counts in an interval without imputation.
```{r}
lm_interval <- data.frame(
start = 0,
end = Inf,
cmax = TRUE,
tmax = TRUE,
cmin = TRUE,
tmin = TRUE,
tfirst = TRUE,
tlast = TRUE,
clast.obs = TRUE,
clast.pred = TRUE,
count_conc = TRUE,
count_conc_measured = TRUE
)
o_nca_lm <- pk.nca(PKNCAdata(o_conc, o_dose, intervals = lm_interval))
as.data.frame(o_nca_lm) |>
filter(PPTESTCD %in% c("cmax","tmax","cmin","tmin","tfirst","tlast",
"clast.obs","clast.pred","count_conc","count_conc_measured")) |>
select(subject, PPTESTCD, PPORRES) |>
arrange(subject, PPTESTCD)
```
**When the maximum occurs at more than one timepoint** (an exact tie), the default is to return the first Tmax. Control this with `first.tmax`:
```{r}
# Use the last Tmax when there are ties
o_data_lasttmax <- PKNCAdata(
o_conc, o_dose,
intervals = ev_intervals,
options = list(first.tmax = FALSE)
)
o_nca_lasttmax <- pk.nca(o_data_lasttmax)
# Tmax is unchanged for every subject:
identical(
as.data.frame(o_nca) |> filter(PPTESTCD == "tmax") |> pull(PPORRES),
as.data.frame(o_nca_lasttmax) |> filter(PPTESTCD == "tmax") |> pull(PPORRES)
)
```
The option has no effect here because no Theoph subject reaches its maximum concentration at two timepoints — `first.tmax` only matters when the maximum is an exact tie. Tie-breaking for Tmin is controlled by the `first.tmin` option (≥ 0.13.0; default `TRUE` = return first occurrence). All calculation options are cataloged in [Options for Controlling PKNCA](https://humanpred.github.io/pknca/articles/v40-options-for-controlling-PKNCA.html).
### AUC variants
The AUC family covers exposure to the last measurable concentration (`auclast`) and exposure extrapolated to infinity using either the observed or the λz-predicted Clast (`aucinf.obs` / `aucinf.pred`), with `aucpext.obs` reporting how much of AUCinf was extrapolated. See the [AUC chapter](auc-methods.qmd) for the full AUC family and the integration methods behind it.
| Parameter | Meaning |
|---|---|
| `auclast` | AUC from 0 to last measurable concentration |
| `aucinf.obs` | AUC extrapolated to ∞ (using observed Clast) |
| `aucinf.pred` | AUC extrapolated to ∞ (using predicted Clast) |
| `aucpext.obs` | % of AUCinf that is extrapolated |
```{r}
auc_params <- data.frame(
start = 0,
end = Inf,
auclast = TRUE,
aucinf.obs = TRUE,
aucinf.pred = TRUE,
aucpext.obs = TRUE
)
o_data_auc <- PKNCAdata(o_conc, o_dose, intervals = auc_params)
o_nca_auc <- pk.nca(o_data_auc)
as.data.frame(o_nca_auc) |>
filter(PPTESTCD %in% c("auclast", "aucinf.obs", "aucinf.pred", "aucpext.obs")) |>
select(subject, PPTESTCD, PPORRES) |>
tidyr::pivot_wider(names_from = PPTESTCD, values_from = PPORRES) |>
arrange(subject)
```
### Half-life and λz
For EV, the terminal phase reflects elimination (not absorption) once absorption is complete. One extravascular-specific caveat: with slow absorption the terminal slope can reflect absorption rather than elimination (flip-flop kinetics), in which case the apparent half-life is an absorption half-life. The usual λz quality controls — enough points in the regression, adjusted R², and span ratio — apply regardless of route; see the [Half-Life chapter](halflife.qmd) for point selection and quality thresholds. The λz point-selection algorithm and quality metrics are detailed in the [Half-Life Calculation vignette](https://humanpred.github.io/pknca/articles/v06-half-life-calculation.html).
```{r}
hl_params <- data.frame(
start = 0,
end = Inf,
lambda.z = TRUE,
half.life = TRUE,
lambda.z.n.points = TRUE,
r.squared = TRUE,
adj.r.squared = TRUE
)
o_data_hl <- PKNCAdata(o_conc, o_dose, intervals = hl_params)
o_nca_hl <- pk.nca(o_data_hl)
as.data.frame(o_nca_hl) |>
filter(PPTESTCD %in% c("half.life", "lambda.z.n.points", "adj.r.squared")) |>
select(subject, PPTESTCD, PPORRES) |>
arrange(subject, PPTESTCD)
```
**Allow or exclude Tmax from the terminal regression:**
By default, the Tmax point is excluded from the λz regression (absorption may still be ongoing). You can override this:
```{r}
o_data_allow_tmax <- PKNCAdata(
o_conc, o_dose,
intervals = hl_params,
options = list(allow.tmax.in.half.life = TRUE)
)
o_nca_allow_tmax <- pk.nca(o_data_allow_tmax)
# Subject 8 is the only subject whose selected regression changes
bind_rows(
as.data.frame(o_nca_hl) |> mutate(version = "default (Tmax excluded)"),
as.data.frame(o_nca_allow_tmax) |> mutate(version = "Tmax allowed")
) |>
filter(subject == "8",
PPTESTCD %in% c("lambda.z.n.points", "adj.r.squared", "half.life")) |>
select(version, PPTESTCD, PPORRES) |>
tidyr::pivot_wider(names_from = PPTESTCD, values_from = PPORRES)
```
For the other 11 subjects the best-fitting regression already excluded Tmax, so nothing changes; subject 8's best fit (highest adjusted R²) now starts at Tmax, using one more point and shifting the half-life slightly.
### Clearance and volume (apparent)
For extravascular dosing the fraction absorbed (F) is unknown, so clearance and volume cannot be separated from it: PKNCA's `cl.*` and `vz.*` values are apparent, i.e. CL/F and Vz/F. The formulas are the same as for IV dosing (Dose / AUC, then CL / λz), with F implicitly in the denominator — both are requested in the basic analysis above.
| Parameter | Meaning |
|---|---|
| `cl.obs` | Apparent clearance = Dose / AUCinf.obs (this is CL/F) |
| `vz.obs` | Apparent volume = CL.obs / λz (this is Vz/F) |
Each clearance and volume is available for every AUC basis, named by its suffix: `.obs` and `.pred` (AUCinf with observed or predicted Clast), `.last` (AUClast), `.all` (AUCall), and `.int.last`, `.int.all`, `.int.inf.obs`, `.int.inf.pred` (the interval AUCs, `aucint.*`). PKNCA 0.13.0 completed these families for `cl.*`, `vz.*`, `vss.*`, `mrt.*`, and `kel.*` (for example `vz.last`, `vss.int.last`, `mrt.int.inf.obs`); the full list is printed at the end of this chapter.
### Lag time (tlag)
`tlag` is the time before absorption begins — the delay between dosing and the first measurable rise in concentration. Common for enteric-coated formulations or SC injections with a diffusion delay.
```{r}
o_nca_tlag <- pk.nca(PKNCAdata(o_conc, o_dose,
intervals = data.frame(start = 0, end = Inf, tlag = TRUE, tmax = TRUE, cmax = TRUE)))
as.data.frame(o_nca_tlag) |>
filter(PPTESTCD %in% c("tlag", "tmax", "cmax")) |>
select(subject, PPTESTCD, PPORRES) |>
tidyr::pivot_wider(names_from = PPTESTCD, values_from = PPORRES) |>
arrange(subject)
```
> For the Theoph dataset, tlag is 0 for all subjects (no lag). It becomes non-zero when the concentration-time profile shows a flat period after dosing before rising.
### Bioavailability (f.obs and related)
Bioavailability (`pk.calc.f()`) compares this administration with a reference administration, so it needs two profiles — see the [Bioavailability](#bioavailability) section below. The parameter formerly named `f` is `f.obs` since PKNCA 0.13.0 (requesting `f` is an error), alongside `f.pred`, `f.last`, and the interval-based `f.int.*`.
### Volume at steady state (Vss)
Vss is computed from CL and MRT, so for extravascular dosing `vss.obs` and `vss.pred` are the apparent Vss (i.e., Vss/F). `vss.last` uses MRTlast instead of the extrapolated MRT.
```{r}
vss_params <- data.frame(
start = 0,
end = Inf,
vss.obs = TRUE, # apparent Vss = CL.obs × MRT.obs
vss.pred = TRUE,
vss.last = TRUE # uses MRTlast
)
o_nca_vss <- pk.nca(PKNCAdata(o_conc, o_dose, intervals = vss_params))
as.data.frame(o_nca_vss) |>
filter(grepl("^vss", PPTESTCD)) |>
select(subject, PPTESTCD, PPORRES) |>
arrange(subject, PPTESTCD)
```
### Mean residence time (MRT)
| Parameter | Meaning |
|---|---|
| `mrt.last` | AUMC(0–last) / AUC(0–last) |
| `mrt.obs` | AUMC(0–∞) / AUC(0–∞), observed Clast |
| `mrt.pred` | AUMC(0–∞) / AUC(0–∞), predicted Clast |
```{r}
mrt_ev_params <- data.frame(
start = 0, end = Inf,
mrt.last = TRUE,
mrt.obs = TRUE,
mrt.pred = TRUE
)
o_nca_mrt_ev <- pk.nca(PKNCAdata(o_conc, o_dose, intervals = mrt_ev_params))
as.data.frame(o_nca_mrt_ev) |>
filter(grepl("^mrt", PPTESTCD)) |>
select(subject, PPTESTCD, PPORRES) |>
arrange(subject, PPTESTCD)
```
### Effective half-life and elimination rate constant
The effective half-life is ln(2) × MRT — a rate that reflects overall persistence rather than the terminal slope. The related `kel.*` parameters are 1/MRT, **not** λz; the two differ whenever distribution or absorption contributes materially to MRT.
All extravascular effective half-life and elimination rate constant variants:
| Parameter | Based on |
|---|---|
| `thalf.eff.last` | `mrt.last` |
| `thalf.eff.obs` | `mrt.obs` |
| `thalf.eff.pred` | `mrt.pred` |
| `kel.obs` | 1 / `mrt.obs` |
| `kel.pred` | 1 / `mrt.pred` |
| `kel.last` | 1 / `mrt.last` |
```{r}
eff_params <- data.frame(
start = 0, end = Inf,
thalf.eff.last = TRUE,
thalf.eff.obs = TRUE,
thalf.eff.pred = TRUE,
kel.obs = TRUE,
kel.pred = TRUE,
kel.last = TRUE
)
o_nca_eff <- pk.nca(PKNCAdata(o_conc, o_dose, intervals = eff_params))
as.data.frame(o_nca_eff) |>
filter(grepl("^(thalf|kel)", PPTESTCD)) |>
select(subject, PPTESTCD, PPORRES) |>
arrange(subject, PPTESTCD)
```
---
## AUC over a specific interval
For multiple-dose studies, request AUC over the dosing interval (tau):
```{r}
# Single dose, but demonstrating syntax for AUC(0-tau)
tau_interval <- data.frame(
start = 0,
end = 24, # tau = 24 h
auclast = TRUE,
cmax = TRUE,
tmax = TRUE
)
o_data_tau <- PKNCAdata(o_conc, o_dose, intervals = tau_interval)
o_nca_tau <- pk.nca(o_data_tau)
as.data.frame(o_nca_tau) |> select(subject, PPTESTCD, PPORRES)
```
---
## Imputation of missing predose concentrations
In some studies, the predose sample is missing or was not collected. PKNCA can impute a time-0 concentration before calculations. Full method descriptions and per-interval examples are in the [Concentration Imputation chapter](imputation.qmd) and the [Data Imputation vignette](https://humanpred.github.io/pknca/articles/v08-data-imputation.html).
Available built-in methods (comma-separate to chain them; `pknca_impute_methods()` lists them):
| Method | What it does |
|---|---|
| `start_predose` | Shifts the most recent measured pre-interval concentration to the interval start, if no concentration exists at the start (nothing is added if there is no pre-interval observation) |
| `start_conc0` | Sets the start concentration to 0, replacing any existing value at the start time |
| `start_predose_conc0` | Uses a predose concentration when one is available and 0 otherwise, keeping a concentration measured at the start time (≥ 0.13.0) |
| `start_cmin` | Adds the minimum concentration in the interval at the start time |
| `end_conc_drop` | Drops a concentration measured exactly at the interval end (≥ 0.13.0) |
```{r}
# Remove the time=0 observation from subject 1 to simulate missing predose
d_conc_missing <- d_conc |>
filter(!(subject == "1" & time == 0))
o_conc_miss <- PKNCAconc(d_conc_missing, conc ~ time | subject)
# With imputation: start_conc0 adds conc = 0 at the interval start
o_data_imputed <- PKNCAdata(
o_conc_miss, o_dose,
intervals = ev_intervals,
impute = "start_conc0"
)
o_nca_imputed <- pk.nca(o_data_imputed)
# Compare subject 1 AUClast: original data vs. missing predose imputed as 0
bind_rows(
as.data.frame(o_nca) |> filter(subject == "1", PPTESTCD == "auclast") |> mutate(version = "original"),
as.data.frame(o_nca_imputed) |> filter(subject == "1", PPTESTCD == "auclast") |> mutate(version = "imputed")
) |>
select(version, PPTESTCD, PPORRES) |>
as.data.frame() # base print shows full precision; the difference is small
```
**Per-interval imputation** — apply different methods to different calculation windows:
```{r}
# Intervals with an "impute" column specifying method per row
d_intervals_impute <- data.frame(
start = 0,
end = Inf,
auclast = TRUE,
impute = "start_conc0"
)
o_data_per_interval <- PKNCAdata(
o_conc_miss, o_dose,
intervals = d_intervals_impute,
impute = "impute" # the name of the intervals column holding the method
)
o_nca_per_interval <- pk.nca(o_data_per_interval)
# Subject 1's missing predose is imputed by the interval row — same result as above
bind_rows(
as.data.frame(o_nca_imputed) |> mutate(version = "global impute"),
as.data.frame(o_nca_per_interval) |> mutate(version = "per-interval impute")
) |>
filter(subject == "1", PPTESTCD == "auclast") |>
select(version, PPTESTCD, PPORRES)
```
With a single interval row the per-interval column reproduces the global setting; with several rows, each row's `impute` value applies only to its own calculation window. The column can have any name (≥ 0.13.0); `PKNCAdata(impute = )` names it: a value that is a column name of the intervals is read as that column, and any other value is the method for every interval.
---
## Bioavailability
Bioavailability (F) compares the dose-normalized exposure of the extravascular dose with an IV reference (absolute F) or another formulation (relative F). Since PKNCA 0.13.0 it is a *secondary parameter* calculated in the same `pk.nca()` run as the AUCs: the intervals carry a treatment (or route, or formulation) grouping column, the test intervals request `f.obs`, and the reference profile is named with `PKNCAdata(group_ref = )`, an `f.obs_ref` pointer, or `interval_add_secondary()`. `f.obs` takes the reference dose and AUCinf,obs from the reference interval and the test dose (`totdose`) and AUCinf,obs from its own interval; `dose2`/`auc2` interval columns, used by older workarounds, are ignored. Variants name the AUC they are built on:
| Parameter | AUC basis |
|---|---|
| `f.obs` | `aucinf.obs` (formerly `f`) |
| `f.pred` | `aucinf.pred` |
| `f.last` | `auclast` |
| `f.int.last`, `f.int.all`, `f.int.obs`, `f.int.pred` | `aucint.last`, `aucint.all`, `aucint.inf.obs`, `aucint.inf.pred` |
Theoph has no reference administration, so the worked example — an IV and oral crossover with `f.obs` and `f.last` checked by hand — is in the [Secondary Parameters chapter](secondary-parameters.qmd#bioavailability). The same chapter covers food-effect ratios (`ratio.cmax`, `ratio.aucinf.obs`) for comparing fed and fasted extravascular doses.
---
## Multiple-dose / steady-state
For multiple-dose data, request steady-state parameters over a tau interval — see the [Multiple-Dose chapter](multiple-dose.qmd) for the full workflow. To simulate steady state from single-dose data, see [Superposition of Pharmacokinetic Data](https://humanpred.github.io/pknca/articles/v20-superposition.html); to estimate when steady state is reached, see [Noncompartmental evaluation of time to steady-state](https://humanpred.github.io/pknca/articles/v22-time-to-steady-state.html). Here the interval syntax is demonstrated on the single-dose Theoph data:
```{r}
ss_intervals <- data.frame(
start = 0,
end = 24,
cmax = TRUE,
cmin = TRUE,
tmax = TRUE,
auclast = TRUE
)
o_data_ss <- PKNCAdata(o_conc, o_dose, intervals = ss_intervals)
o_nca_ss <- pk.nca(o_data_ss)
as.data.frame(o_nca_ss) |>
select(subject, PPTESTCD, PPORRES) |>
arrange(subject, PPTESTCD)
```
---
## Excluding observations
Mark individual timepoints to exclude (e.g. vomiting within 2× median Tmax, suspected contamination):
```{r}
d_conc_excl <- d_conc |>
mutate(
exclude_reason = ifelse(subject == "5" & time == 2.02,
"vomiting within 2x median Tmax", NA_character_)
)
o_conc_excl <- PKNCAconc(d_conc_excl, conc ~ time | subject, exclude = "exclude_reason")
# The flagged row stays in the data with its reason
as.data.frame(o_conc_excl) |> filter(!is.na(exclude_reason))
```
The excluded point is dropped from every parameter calculation, while the flagged row remains in the data with its reason (shown above), preserving the audit trail.
---
## BLQ (below limit of quantification) handling
PKNCA's `conc.blq` option controls how BLQ values (typically entered as 0) are treated:
The default is `list(first = "keep", middle = "drop", last = "keep")`:
- `first` — BLQs before the first non-BLQ concentration (keep, often a predose zero)
- `middle` — BLQs sandwiched between non-BLQ values (dropped by default)
- `last` — BLQs after the last non-BLQ concentration (keep by default)
In Theoph the only BLQ values are the predose zeros, which the default rule keeps, so overriding `conc.blq` changes little on this dataset. To see the mechanism, simulate a mid-profile BLQ — subject 3's 3.62 h sample set to 0 — and compare the default against a global `"keep"`:
```{r}
d_conc_blq <- d_conc |>
mutate(conc = ifelse(subject == "3" & time == 3.62, 0, conc))
o_conc_blq <- PKNCAconc(d_conc_blq, conc ~ time | subject)
auclast_interval <- data.frame(start = 0, end = Inf, auclast = TRUE)
# Default: the middle BLQ is dropped and the AUC interpolates across the gap
o_nca_blq_default <- pk.nca(PKNCAdata(o_conc_blq, o_dose, intervals = auclast_interval))
# Global override: keep all BLQ values as 0
o_data_blq_keep <- PKNCAdata(
o_conc_blq, o_dose,
intervals = auclast_interval,
options = list(conc.blq = "keep")
)
o_nca_blq_keep <- pk.nca(o_data_blq_keep)
bind_rows(
as.data.frame(o_nca_blq_default) |> mutate(version = "default (drop middle BLQ)"),
as.data.frame(o_nca_blq_keep) |> mutate(version = "conc.blq = \"keep\"")
) |>
filter(subject == "3", PPTESTCD == "auclast") |>
select(version, PPTESTCD, PPORRES)
```
Keeping the mid-profile 0 pulls the concentration curve down to 0 at 3.62 h and cuts a wedge out of subject 3's AUC; the default treats an isolated BLQ between measurable concentrations as an assay artifact and interpolates across it.
Overrides can also be per-position (`first`/`middle`/`last`) or relative to Tmax (`before.tmax`/`after.tmax`), and the action can be `"keep"`, `"drop"`, or a numeric replacement value. The objects are built the same way; running them on this data gives the results described below:
```{r}
# Global: remove all BLQ values
o_data_blq_drop <- PKNCAdata(
o_conc, o_dose,
intervals = ev_intervals,
options = list(conc.blq = "drop")
)
# Per-phase (before/after Tmax):
o_data_blq <- PKNCAdata(
o_conc, o_dose,
intervals = ev_intervals,
options = list(
conc.blq = list(
before.tmax = "keep", # keep 0s before Tmax (ascending phase)
after.tmax = "drop" # drop BLQs after Tmax
)
)
)
```
On Theoph the per-phase rule reproduces the default results (the predose zeros sit before Tmax and are kept), while the global `"drop"` is a trap for intervals starting at the dose time: it removes the predose zeros too, so AUCs that start at 0 begin before the first remaining measurement and return `NA` with a warning.
To use BLQ observations in half-life estimation rather than dropping them, see [Half-life calculation with Tobit regression](https://humanpred.github.io/pknca/articles/v06-half-life-calculation-tobit.html) (≥ 0.13.0).
---
## AUC integration methods
The default integration method is **lin up/log down** (linear interpolation on the ascending phase, log-linear on the descending phase). The alternatives, `"linear"` and `"lin-log"`, are selected per-analysis with the `auc.method` option. See the [AUC chapter](auc-methods.qmd) for how each method computes the trapezoids and when to prefer which. Formal definitions of each rule are in the [AUC integration methods vignette](https://humanpred.github.io/pknca/articles/v23-auc-integration-methods.html).
```{r}
# Switch to the linear trapezoidal method
o_data_lin <- PKNCAdata(o_conc, o_dose, intervals = ev_intervals,
options = list(auc.method = "linear"))
as.data.frame(pk.nca(o_data_lin)) |>
filter(PPTESTCD == "auclast") |>
select(subject, PPTESTCD, PPORRES) |>
head(3)
```
---
## Units
Assign units to get automatic unit propagation through derived parameters. See the [Units chapter](units.qmd) for the full units system, including unit conversion and per-parameter overrides. See also [Unit Assignment and Conversion with PKNCA](https://humanpred.github.io/pknca/articles/v07-unit-conversion.html) for the full units workflow.
```{r}
units_table <- pknca_units_table(
concu = "mg/L",
timeu = "h",
doseu = "mg",
amountu = "mg"
)
o_conc_u <- PKNCAconc(d_conc, conc ~ time | subject, concu = "mg/L", timeu = "h")
o_dose_u <- PKNCAdose(d_dose, dose ~ time | subject, route = "extravascular",
doseu = "mg", timeu = "h")
o_data_u <- PKNCAdata(o_conc_u, o_dose_u, intervals = ev_intervals, units = units_table)
o_nca_u <- pk.nca(o_data_u)
as.data.frame(o_nca_u) |>
filter(PPTESTCD %in% c("auclast", "aucinf.obs", "cl.obs", "half.life")) |>
select(subject, PPTESTCD, PPORRES) |>
arrange(subject, PPTESTCD)
```
---
## Summary with custom statistics
`summary()` produces a study-report-style table of summary statistics per parameter, and `PKNCA.set.summary()` customizes the statistics reported for any parameter — see [Post-processing](postprocessing.qmd) for the full summary workflow. More summary and exclusion workflows are in the [Post-Processing vignette](https://humanpred.github.io/pknca/articles/v07-post-processing.html).
```{r}
PKNCA.set.summary(
"tmax",
description = "median [min, max]",
point = median,
spread = function(x) c(min(x), max(x)) # must return numeric; PKNCA formats the string
)
summary(o_nca)
```
---
## Full parameter list for extravascular
All extravascular-relevant parameters you can request in an interval:
```{r}
interval_cols <- get.interval.cols()
ev_relevant <- c(
# Concentration / time landmarks
"cmax", "tmax", "cmin", "tmin", "tfirst", "tlast", "tlag",
"clast.obs", "clast.pred", "count_conc", "count_conc_measured",
# AUC family
"auclast", "aucall", "aucinf.obs", "aucinf.pred",
"aucpext.obs", "aucpext.pred",
"aucint.last", "aucint.all", "aucint.inf.obs", "aucint.inf.pred",
# AUMC
"aumclast", "aumcall", "aumcinf.obs", "aumcinf.pred",
"aumcint.last", "aumcint.all", "aumcint.inf.obs", "aumcint.inf.pred",
# Half-life / λz
"half.life", "lambda.z", "lambda.z.n.points", "lambda.z.time.first", "lambda.z.time.last",
"r.squared", "adj.r.squared", "span.ratio",
# Clearance (apparent)
"cl.obs", "cl.pred", "cl.last", "cl.all",
"cl.int.last", "cl.int.all", "cl.int.inf.obs", "cl.int.inf.pred",
# Volume (apparent)
"vz.obs", "vz.pred", "vz.last", "vz.all",
"vz.int.last", "vz.int.all", "vz.int.inf.obs", "vz.int.inf.pred",
"vss.obs", "vss.pred", "vss.last", "vss.all",
"vss.int.last", "vss.int.all", "vss.int.inf.obs", "vss.int.inf.pred",
# MRT
"mrt.last", "mrt.obs", "mrt.pred", "mrt.all",
"mrt.int.last", "mrt.int.all", "mrt.int.inf.obs", "mrt.int.inf.pred",
# Effective half-life / kel
"thalf.eff.last", "thalf.eff.obs", "thalf.eff.pred",
"kel.obs", "kel.pred", "kel.last", "kel.all",
"kel.int.last", "kel.int.all", "kel.int.inf.obs", "kel.int.inf.pred",
# Bioavailability (secondary parameters; need a reference interval)
"f.obs", "f.pred", "f.last", "f.int.last", "f.int.all", "f.int.obs", "f.int.pred",
# Dose-normalized
"auclast.dn", "aucinf.obs.dn", "cmax.dn", "clast.obs.dn"
)
sort(names(interval_cols)[names(interval_cols) %in% ev_relevant])
# Every name above is registered
setdiff(ev_relevant, names(interval_cols))
```
This list is curated. `pknca_parameter_table()` (≥ 0.13.0) classifies every registered parameter, including the routes it applies to, so the complete catalog for extravascular dosing is one filter away:
```{r}
d_param_table <- pknca_parameter_table()
d_ev_params <- d_param_table[grepl("extravascular", d_param_table$route), ]
nrow(d_ev_params)
head(d_ev_params[, c("parameter", "concept", "tier", "sparse", "secondary")])
```
The `sparse` column flags outputs that apply to sparse sampling (such as the standard error `auclast_se`), and `secondary` flags parameters that need a reference interval, such as `f.obs`.
---
::: {.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) · [pk.calc.f()](https://humanpred.github.io/pknca/reference/pk.calc.f.html) · [pknca_parameter_table()](https://humanpred.github.io/pknca/reference/pknca_parameter_table.html) · [pknca_units_table()](https://humanpred.github.io/pknca/reference/pknca_units_table.html) · [PKNCA.set.summary()](https://humanpred.github.io/pknca/reference/PKNCA.set.summary.html) · [get.interval.cols()](https://humanpred.github.io/pknca/reference/get.interval.cols.html) · [summary()](https://humanpred.github.io/pknca/reference/summary.PKNCAresults.html) · [PKNCA_impute_method_start_*()](https://humanpred.github.io/pknca/reference/PKNCA_impute_method.html) · [pknca_impute_methods()](https://humanpred.github.io/pknca/reference/pknca_impute_methods.html)
:::