9  Urine Excretion

9.1 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

9.2 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. Each row covers the collection period from the previous sample to the current time.

# 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(
    start_time = c(0, 2, 6, 12)[match(end_time, c(2, 6, 12, 24))],
    # 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)
  Subject time      conc volume
1       1    2 138.40812    209
2       2    2 251.02891    105
3       3    2 120.53715    231
4       4    2 110.38941    298
5       1    6 136.09722    198
6       2    6  66.39376    293
# 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"
)

9.3 Setting up urine PKNCAconc

Use concu for concentration units and volumeu for the urine volume column name. The formula uses the same conc ~ time | Subject structure.

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")
Found column named route, using it for the attribute of the same name.

9.4 Computing ae, fe, and renal clearance

Request urine parameters in the intervals data frame. The interval end should match the last urine collection time.

urine_intervals <- data.frame(
  start     = 0,
  end       = 24,
  ae        = TRUE,
  fe        = TRUE,
  clr.obs   = TRUE,
  clr.last  = TRUE,
  # Also request plasma AUC for the combined object
  auclast   = TRUE,
  aucinf.obs = TRUE
)

# PKNCAdata accepts a list of conc objects when plasma and urine are separate
o_data_ur <- PKNCAdata(
  o_conc_plasma,
  o_dose,
  intervals = urine_intervals,
  impute = "start_conc0"
)

# Add urine data
o_data_ur$conc <- list(plasma = o_conc_plasma, urine = o_conc_urine)

Note: PKNCA 0.12.1 support for combined plasma+urine in a single PKNCAdata is limited. The most reliable workflow is to run plasma NCA and urine NCA separately and join the results.


9.5 Separate plasma + urine workflow

This approach works reliably in PKNCA 0.12.1:

# --- 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
# A tibble: 4 × 7
  Subject auclast aucinf.obs ae_total    fe clr_obs clr_last
  <fct>     <dbl>      <dbl>    <dbl> <dbl>   <dbl>    <dbl>
1 1          29.0       35.2     91.8 0.918    2.61     3.17
2 2          30.8       33.1     78.8 0.788    2.38     2.56
3 3          32.7       36.5     82.2 0.822    2.25     2.51
4 4          30.5       36.1     92.9 0.929    2.57     3.05

9.6 Interpreting results

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)"
  )
Urine NCA results — simulated IV bolus (dose = 100 mg)
Subject Ae (mg) fe CLr.obs (L/h) CLr.last (L/h)
1 91.844 0.918 2.609 3.172
2 78.781 0.788 2.382 2.558
3 82.223 0.822 2.252 2.511
4 92.938 0.929 2.573 3.049

Interpretation guide:

  • fe near 0.30 means ~30% of dose is renally excreted unchanged. The remainder is metabolized or excreted by other routes.
  • clr.obs ≈ GFR (120 mL/min = 7.2 L/h) suggests renal filtration; higher values indicate active secretion.
  • clr.last < clr.obs is expected when the sampling window misses late excretion.

9.7 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:

# Structure for direct ae computation
o_conc_ur2 <- PKNCAconc(
  urine_collections,
  conc ~ time | Subject,
  volume = "volume"
)

ur_intervals <- data.frame(
  start = 0,
  end   = 24,
  ae    = TRUE
)

o_data_ae <- PKNCAdata(o_conc_ur2, 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)
# A tibble: 4 × 2
  Subject PPORRES
  <fct>     <dbl>
1 1        91844.
2 2        78781.
3 3        82223.
4 4        92938.

9.8 New urine parameters in PKNCA ≥ 0.12.2

9.8.1 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:

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_ur2, o_dose, intervals = vol_interval))
as.data.frame(o_nca_vol) |>
  filter(PPTESTCD %in% c("ae", "volpk")) |>
  select(Subject, PPTESTCD, PPORRES)

9.8.2 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).

er_interval <- data.frame(
  start  = 0,
  end    = 24,
  ae     = TRUE,
  ermax  = TRUE,    # maximum excretion rate
  ertmax = TRUE,    # time of maximum excretion rate
  ertlst = TRUE     # time of last measurable excretion rate
)

o_nca_er <- pk.nca(PKNCAdata(o_conc_ur2, o_dose, intervals = er_interval))
as.data.frame(o_nca_er) |>
  filter(PPTESTCD %in% c("ae", "ermax", "ertmax", "ertlst")) |>
  select(Subject, PPTESTCD, PPORRES)

9.8.3 Dose-normalized renal clearance (≥ 0.12.2)

Dose-normalized renal clearance variants are now available in the parameter registry:

Parameter Normalized from
clr.last.dn clr.last / dose
clr.obs.dn clr.obs / dose
clr.pred.dn clr.pred / dose

Request them in the interval data frame alongside their un-normalized counterparts:

dn_interval <- data.frame(
  start        = 0,
  end          = Inf,
  ae           = TRUE,
  fe           = TRUE,
  clr.last     = TRUE,
  clr.last.dn  = TRUE,
  clr.obs      = TRUE,
  clr.obs.dn   = TRUE
)

pkgdown reference: PKNCAconc() · PKNCAdose() · PKNCAdata() · pk.nca()