---
title: "Urine Excretion"
---
```{r setup, include=FALSE}
library(PKNCA)
library(dplyr)
library(ggplot2)
conflicted::conflicts_prefer(dplyr::filter, dplyr::select, .quiet = TRUE)
```
## Urine NCA overview
Urine NCA characterizes how much drug is excreted in urine over time. Key parameters:
| Parameter | Formula | Meaning |
|---|---|---|
| `ae` | Σ(concentration × volume) | Amount excreted in urine (mass) |
| `fe` | ae / dose | Fraction of dose excreted unchanged |
| `clr.obs` | ae / AUCinf.obs | Renal clearance — using observed Clast |
| `clr.pred` | ae / AUCinf.pred | Renal clearance — using predicted Clast |
| `clr.last` | ae / AUClast | Renal clearance — no extrapolation |
---
## Data structure for urine
Urine NCA requires a **separate concentration object** for urine, with the `volume` argument to `PKNCAconc()` specifying the column name that holds the urine volume collected in each interval.
The key distinction from plasma NCA: urine concentrations represent **interval averages** rather than timepoint measurements. This dataset records each collection's **end** time as `time` — sufficient for amount parameters like `ae`, which only need each row to fall inside the analysis interval. PKNCA's full urine convention is `time` as the collection **start** plus a `duration` column giving the collection length; the excretion-rate section below rebuilds the data that way.
```{r}
# Simulate urine collection for 4 subjects after a 100 mg IV bolus
# Collections at: 0-2h, 2-6h, 6-12h, 12-24h
set.seed(99)
urine_collections <- expand.grid(
Subject = factor(1:4),
end_time = c(2, 6, 12, 24)
) |>
mutate(
# Simulated urine volume (mL) and concentration (mcg/mL)
volume_mL = round(runif(n(), 80, 300)),
# Fraction excreted: ~30% in first 2h, 25% 2-6h, 20% 6-12h, 15% 12-24h
ae_target = 100 * c(0.30, 0.25, 0.20, 0.15)[match(end_time, c(2, 6, 12, 24))],
ae_indiv = ae_target * exp(rnorm(n(), 0, 0.1)), # mild IIV
conc_mcg_mL = ae_indiv / volume_mL * 1000 # conc = amount / volume
) |>
select(Subject, time = end_time, conc = conc_mcg_mL, volume = volume_mL)
head(urine_collections)
```
Renal clearance needs a plasma AUC, so we also simulate matched plasma profiles.
```{r}
# Plasma concentrations (IV bolus, same subjects)
ke <- 0.1 # h^-1
d_plasma <- expand.grid(
Subject = factor(1:4),
time = c(0.5, 1, 2, 4, 6, 8, 12, 16, 24)
) |>
mutate(
ke_i = ke * exp(rnorm(n(), 0, 0.15)),
conc = 100 / 30 * exp(-ke_i * time) # dose/V * e^(-ke*t)
)
d_dose <- data.frame(
Subject = factor(1:4),
dose = 100, # mg
time = 0,
route = "intravascular"
)
```
---
## Setting up urine PKNCAconc
Pass the urine volume column name via the `volume` argument; the unit arguments (`concu`, `amountu`, `timeu`, …) are optional and set units. For assigning and converting units, see [Unit Assignment and Conversion with PKNCA](https://humanpred.github.io/pknca/articles/v07-unit-conversion.html). The formula uses the same `conc ~ time | Subject` structure.
```{r}
o_conc_plasma <- PKNCAconc(d_plasma, conc ~ time | Subject)
o_conc_urine <- PKNCAconc(
urine_collections,
conc ~ time | Subject,
volume = "volume" # column with urine volume (mL)
)
o_dose <- PKNCAdose(d_dose, dose ~ time | Subject, route = "intravascular")
```
---
## Separate plasma + urine workflow
Renal clearance combines the urine amount excreted (Ae) with the plasma AUC. Combined plasma+urine analysis in a single `PKNCAdata` object is limited; the recommended workflow is to run plasma NCA and urine NCA separately and join the results.
This separate-workflow approach works reliably:
```{r}
# --- Plasma NCA ---
o_plasma_data <- PKNCAdata(
o_conc_plasma, o_dose,
intervals = data.frame(start = 0, end = Inf,
auclast = TRUE, aucinf.obs = TRUE),
impute = "start_conc0"
)
o_nca_plasma <- pk.nca(o_plasma_data)
plasma_auc <- as.data.frame(o_nca_plasma) |>
filter(PPTESTCD %in% c("auclast", "aucinf.obs")) |>
select(Subject, PPTESTCD, PPORRES) |>
tidyr::pivot_wider(names_from = PPTESTCD, values_from = PPORRES)
# --- Urine: compute ae manually ---
# ae = concentration (mcg/mL) × volume (mL) / 1000 → mg
ae_by_subject <- urine_collections |>
mutate(ae_interval = conc * volume / 1000) |> # mcg/mL * mL / 1000 = mg
group_by(Subject) |>
summarise(ae_total = sum(ae_interval), .groups = "drop")
# --- Join and compute clr, fe ---
dose_amt <- 100 # mg
results <- plasma_auc |>
left_join(ae_by_subject, by = "Subject") |>
mutate(
fe = ae_total / dose_amt,
clr_obs = ae_total / aucinf.obs,
clr_last = ae_total / auclast
)
results
```
`clr.pred` works identically using AUCinf.pred as the denominator.
For a real IV analysis, back-extrapolate C0 instead — see the [Intravascular](intravascular.qmd) chapter; `conc = 0` at `t = 0` is used here only to keep the simulation short.
---
## Interpreting results
```{r}
results |>
mutate(across(where(is.numeric), \(x) round(x, 3))) |>
select(Subject, ae_total, fe, clr_obs, clr_last) |>
knitr::kable(
col.names = c("Subject", "Ae (mg)", "fe", "CLr.obs (L/h)", "CLr.last (L/h)"),
caption = "Urine NCA results — simulated IV bolus (dose = 100 mg)"
)
```
**Interpretation guide:**
- `fe` near 0.9 means ~90% of the dose is renally excreted unchanged — renal excretion dominates elimination for this simulated drug. A low `fe` would instead point to metabolism or other elimination routes.
- `clr.obs` ≈ GFR (120 mL/min = 7.2 L/h) suggests renal filtration; higher values indicate active secretion.
- `clr.last` > `clr.obs` because the same Ae is divided by the smaller AUClast; the gap grows when sampling misses late elimination.
---
## Using the PKNCA `ae` parameter natively
If your data are structured with urine as a separate concentration object and `PKNCAconc(..., volume = "vol_col")`, PKNCA can compute `ae` directly:
```{r}
# o_conc_urine (created above) already carries the volume column
ur_intervals <- data.frame(
start = 0,
end = 24,
ae = TRUE
)
o_data_ae <- PKNCAdata(o_conc_urine, o_dose, intervals = ur_intervals)
o_nca_ae <- pk.nca(o_data_ae)
as.data.frame(o_nca_ae) |>
filter(PPTESTCD == "ae") |>
select(Subject, PPORRES) |>
arrange(Subject)
```
Note the units: `ae` here is in mcg (urine concentration in mcg/mL × volume in mL), so ~90,000 mcg matches the ~90 mg computed manually above.
---
## New urine parameters (≥ 0.12.2)
### volpk — total collection volume
`volpk` is the sum of urine volumes across all collection periods within the analysis interval. When you pass `volume = "volume"` to `PKNCAconc()`, PKNCA can compute this automatically:
```{r}
vol_interval <- data.frame(
start = 0,
end = 24,
ae = TRUE,
volpk = TRUE # sum of urine volumes over the interval
)
o_nca_vol <- pk.nca(PKNCAdata(o_conc_urine, o_dose, intervals = vol_interval))
as.data.frame(o_nca_vol) |>
filter(PPTESTCD %in% c("ae", "volpk")) |>
select(Subject, PPTESTCD, PPORRES)
```
### Excretion rate parameters
| Parameter | Description |
|---|---|
| `ermax` | Maximum excretion rate (amount/time) within the interval |
| `ertmax` | Midpoint collection time of the maximum excretion rate period |
| `ertlst` | Midpoint collection time of the last measurable excretion rate |
These mirror the plasma-side `cmax` / `tmax` / `tlast` but apply to the excretion rate (amount excreted per collection period divided by collection duration).
```{r}
# Excretion rate needs each collection's duration, and PKNCA takes urine `time` as
# the collection START with `duration` extending forward — rebuild the object that way
urine_er <- urine_collections |>
mutate(
start_t = c(0, 2, 6, 12)[match(time, c(2, 6, 12, 24))],
duration = time - start_t
) |>
transmute(Subject, time = start_t, conc, volume, duration)
o_conc_er <- PKNCAconc(urine_er, conc ~ time | Subject,
volume = "volume", duration = "duration")
er_interval <- data.frame(
start = 0,
end = 24,
ae = TRUE,
ermax = TRUE, # maximum excretion rate
ertmax = TRUE, # midpoint time of the maximum-rate collection
ertlst = TRUE # midpoint time of the last measurable-rate collection
)
o_nca_er <- pk.nca(PKNCAdata(o_conc_er, o_dose, intervals = er_interval))
as.data.frame(o_nca_er) |>
filter(PPTESTCD %in% c("ae", "ermax", "ertmax", "ertlst")) |>
select(Subject, PPTESTCD, PPORRES)
```
### Dose-normalized renal clearance
Dose-normalized renal clearance variants are now available in the parameter registry:
| Parameter | Definition |
|---|---|
| `clr.last.dn` | `clr.last / dose` |
| `clr.obs.dn` | `clr.obs / dose` |
| `clr.pred.dn` | `clr.pred / dose` |
Renal clearance divides the urine Ae by the plasma AUC — two different concentration objects — so a single `pk.nca()` call cannot compute `clr.*` directly. Compute each piece with `pk.nca()` and combine them, with `pk.calc.dn()` applying the dose normalization:
```{r}
o_nca_ae_dn <- pk.nca(PKNCAdata(o_conc_urine, o_dose,
intervals = data.frame(start = 0, end = 24, ae = TRUE)))
clr_dn <- plasma_auc |> # plasma pk.nca() results from the separate workflow above
left_join(
as.data.frame(o_nca_ae_dn) |>
filter(PPTESTCD == "ae") |>
select(Subject, ae = PPORRES),
by = "Subject"
) |>
mutate(
ae = ae / 1000, # native ae is in mcg (mcg/mL * mL); convert to mg to match the plasma AUC
clr.last = ae / auclast,
clr.obs = ae / aucinf.obs,
clr.last.dn = pk.calc.dn(clr.last, 100), # dose = 100 mg
clr.obs.dn = pk.calc.dn(clr.obs, 100)
)
clr_dn
```
---
::: {.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.dn()](https://humanpred.github.io/pknca/reference/pk.calc.dn.html)
:::