7  Intravascular (IV) Examples

7.1 The IV dataset: Indomethacin

We use datasets::Indometh — IV bolus indomethacin in 6 subjects, sampled over 8 hours.

head(Indometh)
Grouped Data: conc ~ time | Subject
  Subject time conc
1       1 0.25 1.50
2       1 0.50 0.94
3       1 0.75 0.78
4       1 1.00 0.48
5       1 1.25 0.37
6       1 2.00 0.19
str(Indometh)
Classes 'nfnGroupedData', 'nfGroupedData', 'groupedData' and 'data.frame':  66 obs. of  3 variables:
 $ Subject: Ord.factor w/ 6 levels "1"<"4"<"2"<"5"<..: 1 1 1 1 1 1 1 1 1 1 ...
 $ time   : num  0.25 0.5 0.75 1 1.25 2 3 4 5 6 ...
 $ conc   : num  1.5 0.94 0.78 0.48 0.37 0.19 0.12 0.11 0.08 0.07 ...
 - attr(*, "formula")=Class 'formula'  language conc ~ time | Subject
  .. ..- attr(*, ".Environment")=<environment: R_EmptyEnv> 
 - attr(*, "labels")=List of 2
  ..$ x: chr "Time since drug administration"
  ..$ y: chr "Indomethacin concentration"
 - attr(*, "units")=List of 2
  ..$ x: chr "(hr)"
  ..$ y: chr "(mcg/ml)"

Columns: Subject (factor, 6 levels), time (h), conc (mcg/mL).

We add dose information. The standard IV bolus dose for this study was 25 mg.

d_conc <- as.data.frame(Indometh)

d_dose <- data.frame(
  Subject = unique(d_conc$Subject),
  dose    = 25,   # mg
  time    = 0,
  route   = "intravascular"
)

# Quick look at concentration profiles
ggplot(d_conc, aes(x = time, y = conc, group = Subject, colour = Subject)) +
  geom_line() + geom_point() +
  scale_y_log10() +
  labs(title = "Indomethacin IV — log-linear concentration profiles",
       x = "Time (h)", y = "Concentration (mcg/mL)") +
  theme_minimal()

The log-linear profiles confirm monoexponential decline — well suited for IV NCA.


7.2 Basic IV analysis

o_conc <- PKNCAconc(d_conc, conc ~ time | Subject)

o_dose <- PKNCAdose(d_dose, dose ~ time | Subject, route = "intravascular")
Found column named route, using it for the attribute of the same name.
# Indometh: no sample at t = 0 (first sample is at 0.25h).
# Use impute = "start_conc0" to add conc = 0 at t = 0 so the interval can start at 0.
# This slightly underestimates AUClast vs. true C0 back-extrapolation (see C0 section below).
first_t <- 0  # interval starts at 0 with imputed C0 = 0

# Request the core IV parameters
iv_intervals <- data.frame(
  start       = first_t,
  end         = Inf,
  auclast     = TRUE,
  aucinf.obs  = TRUE,
  half.life   = TRUE,
  cmax        = TRUE,
  tmax        = TRUE,
  lambda.z    = TRUE,
  cl.obs      = TRUE,
  vz.obs      = TRUE,
  mrt.iv.last = TRUE,
  mrt.iv.obs  = TRUE
)

o_data <- PKNCAdata(o_conc, o_dose, intervals = iv_intervals, impute = "start_conc0")
o_nca  <- pk.nca(o_data)

as.data.frame(o_nca) |>
  select(Subject, PPTESTCD, PPORRES) |>
  arrange(Subject, PPTESTCD)
# A tibble: 132 × 3
   Subject PPTESTCD      PPORRES
   <ord>   <chr>           <dbl>
 1 1       adj.r.squared  0.994 
 2 1       aucinf.obs     2.04  
 3 1       auclast        1.72  
 4 1       aumcinf.obs    7.82  
 5 1       aumclast       3.30  
 6 1       cl.obs        12.3   
 7 1       clast.obs      0.05  
 8 1       clast.pred     0.0502
 9 1       cmax           1.5   
10 1       half.life      4.38  
# ℹ 122 more rows

7.3 Parameter reference: what each one means

7.3.1 Cmax, Tmax, Cmin, Tfirst, Tlast, Clast

For IV bolus, Cmax is the first measured concentration (no absorption phase). Tmax is the time of that first sample.

Parameter Meaning
cmax Maximum observed concentration
tmax Time of Cmax
cmin Minimum observed concentration in the interval
tfirst First time with a non-zero (or 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 Total number of concentration observations (including BLQ)
count_conc_measured Number of observations above the LLOQ
lambda.z.time.last Last timepoint included in the λz regression
landmark_params <- data.frame(
  start               = first_t,
  end                 = Inf,
  cmax                = TRUE,
  tmax                = TRUE,
  cmin                = 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 = landmark_params, impute = "start_conc0"))

as.data.frame(o_nca_lm) |>
  filter(PPTESTCD %in% c("cmax","tmax","cmin","tfirst","tlast",
                          "clast.obs","clast.pred","count_conc","count_conc_measured")) |>
  select(Subject, PPTESTCD, PPORRES) |>
  arrange(Subject, PPTESTCD)
# A tibble: 54 × 3
   Subject PPTESTCD            PPORRES
   <ord>   <chr>                 <dbl>
 1 1       clast.obs            0.05  
 2 1       clast.pred           0.0502
 3 1       cmax                 1.5   
 4 1       cmin                 0     
 5 1       count_conc          12     
 6 1       count_conc_measured 11     
 7 1       tfirst               0.25  
 8 1       tlast                8     
 9 1       tmax                 0.25  
10 4       clast.obs            0.07  
# ℹ 44 more rows

7.3.2 C0 — extrapolated initial concentration

For IV bolus, the true concentration at time 0 is not directly measured (the first sample is taken minutes later). PKNCA extrapolates C0 by back-extrapolating the log-linear terminal phase.

iv_with_c0 <- data.frame(
  start    = first_t,
  end      = Inf,
  c0       = TRUE,
  auclast  = TRUE,
  aucinf.obs = TRUE
)

o_data_c0 <- PKNCAdata(o_conc, o_dose, intervals = iv_with_c0, impute = "start_conc0")
o_nca_c0  <- pk.nca(o_data_c0)

as.data.frame(o_nca_c0) |>
  filter(PPTESTCD %in% c("c0", "cmax", "auclast", "aucinf.obs")) |>
  select(Subject, PPTESTCD, PPORRES) |>
  arrange(Subject, PPTESTCD)
# A tibble: 18 × 3
   Subject PPTESTCD   PPORRES
   <ord>   <chr>        <dbl>
 1 1       aucinf.obs    2.04
 2 1       auclast       1.72
 3 1       c0            2.39
 4 4       aucinf.obs    2.61
 5 4       auclast       2.44
 6 4       c0            2.46
 7 2       aucinf.obs    3.15
 8 2       auclast       2.89
 9 2       c0            2.53
10 5       aucinf.obs    2.16
11 5       auclast       1.92
12 5       c0            4.04
13 6       aucinf.obs    3.10
14 6       auclast       2.84
15 6       c0            3.71
16 3       aucinf.obs    3.07
17 3       auclast       2.88
18 3       c0            4.97

Note: C0 > Cmax is expected for IV bolus — Cmax is the first measured point, C0 is extrapolated back to time 0.

7.3.3 AUC variants

Parameter Meaning
auclast AUC from 0 to last measurable concentration (trapezoidal)
aucinf.obs AUC extrapolated to ∞, using observed Clast
aucinf.pred AUC extrapolated to ∞, using predicted Clast from λz fit
aucpext.obs % of AUCinf that is extrapolated (quality check)
auc_params <- data.frame(
  start       = first_t,
  end         = Inf,
  auclast     = TRUE,
  aucinf.obs  = TRUE,
  aucinf.pred = TRUE,
  aucpext.obs = TRUE
)

o_data_auc <- PKNCAdata(o_conc, o_dose, intervals = auc_params, impute = "start_conc0")
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) |>
  arrange(Subject, PPTESTCD)
# A tibble: 24 × 3
   Subject PPTESTCD    PPORRES
   <ord>   <chr>         <dbl>
 1 1       aucinf.obs     2.04
 2 1       aucinf.pred    2.04
 3 1       auclast        1.72
 4 1       aucpext.obs   15.5 
 5 4       aucinf.obs     2.61
 6 4       aucinf.pred    2.52
 7 4       auclast        2.44
 8 4       aucpext.obs    6.26
 9 2       aucinf.obs     3.15
10 2       aucinf.pred    3.14
# ℹ 14 more rows

Quality check: aucpext.obs > 20% (the default max.aucinf.pext threshold) means too much of the AUC is extrapolated — the study may not have sampled long enough.

7.3.4 Half-life and λz

λz (lambda.z) is the terminal elimination rate constant estimated from log-linear regression of the terminal phase. Half-life = ln(2) / λz.

hl_params <- data.frame(
  start        = first_t,
  end          = Inf,
  lambda.z     = TRUE,
  half.life    = TRUE,
  r.squared    = TRUE,
  lambda.z.n.points = TRUE
)

o_data_hl <- PKNCAdata(o_conc, o_dose, intervals = hl_params, impute = "start_conc0")
o_nca_hl  <- pk.nca(o_data_hl)

as.data.frame(o_nca_hl) |>
  filter(PPTESTCD %in% c("lambda.z", "half.life", "r.squared", "lambda.z.n.points")) |>
  select(Subject, PPTESTCD, PPORRES) |>
  arrange(Subject, PPTESTCD)
# A tibble: 24 × 3
   Subject PPTESTCD          PPORRES
   <ord>   <chr>               <dbl>
 1 1       half.life           4.38 
 2 1       lambda.z            0.158
 3 1       lambda.z.n.points   3    
 4 1       r.squared           0.997
 5 4       half.life           1.62 
 6 4       lambda.z            0.429
 7 4       lambda.z.n.points  10    
 8 4       r.squared           0.867
 9 2       half.life           2.29 
10 2       lambda.z            0.302
# ℹ 14 more rows

Controlling half-life quality:

# Stricter: require at least 4 points and R² ≥ 0.95
o_data_strict <- PKNCAdata(
  o_conc, o_dose,
  intervals = hl_params,
  impute = "start_conc0",
  options = list(
    min.hl.points    = 4,
    min.hl.r.squared = 0.95
  )
)
o_nca_strict <- pk.nca(o_data_strict)

as.data.frame(o_nca_strict) |>
  filter(PPTESTCD %in% c("half.life", "r.squared", "lambda.z.n.points")) |>
  select(Subject, PPTESTCD, PPORRES) |>
  arrange(Subject, PPTESTCD)
# A tibble: 18 × 3
   Subject PPTESTCD          PPORRES
   <ord>   <chr>               <dbl>
 1 1       half.life           3.80 
 2 1       lambda.z.n.points   5    
 3 1       r.squared           0.980
 4 4       half.life           1.62 
 5 4       lambda.z.n.points  10    
 6 4       r.squared           0.867
 7 2       half.life           2.29 
 8 2       lambda.z.n.points   9    
 9 2       r.squared           0.948
10 5       half.life           2.74 
11 5       lambda.z.n.points   8    
12 5       r.squared           0.875
13 6       half.life           1.96 
14 6       lambda.z.n.points   9    
15 6       r.squared           0.904
16 3       half.life           1.64 
17 3       lambda.z.n.points  10    
18 3       r.squared           0.876

Excluding specific points from λz:

Use exclude_half.life column in your concentration data to mark points that should not be included in the terminal regression:

d_conc_excl <- d_conc |>
  mutate(excl_hl = ifelse(Subject == "1" & time < 0.5, TRUE, NA))

o_conc_excl <- PKNCAconc(
  d_conc_excl,
  conc ~ time | Subject,
  exclude_half.life = "excl_hl"
)

7.3.5 Clearance and Volume

For IV, apparent clearance and volume can be calculated directly (no bioavailability factor needed).

Parameter Formula Meaning
cl.obs Dose / AUCinf.obs Total body clearance (L/h)
cl.pred Dose / AUCinf.pred CL using predicted Clast
cl.last Dose / AUClast CL using AUClast only (no extrapolation)
cl.all Dose / AUCall CL using AUCall (BLQs as zero)
vz.obs CL.obs / λz Volume of distribution — terminal phase
vz.pred CL.pred / λz Vz using predicted Clast
vss.obs CL.obs × MRT Volume at steady state (observed)
vss.pred CL.pred × MRT Volume at steady state (predicted)
vss.iv.obs CL.obs × MRT.iv Vss corrected for IV bolus input
cl_params <- data.frame(
  start      = first_t,
  end        = Inf,
  cl.obs     = TRUE,
  cl.pred    = TRUE,
  cl.last    = TRUE,
  vz.obs     = TRUE,
  vz.pred    = TRUE,
  vss.obs    = TRUE,
  vss.iv.obs = TRUE
)

o_data_cl <- PKNCAdata(o_conc, o_dose, intervals = cl_params, impute = "start_conc0")
o_nca_cl  <- pk.nca(o_data_cl)

as.data.frame(o_nca_cl) |>
  filter(PPTESTCD %in% c("cl.obs", "cl.pred", "cl.last", "vz.obs", "vss.obs", "vss.iv.obs")) |>
  select(Subject, PPTESTCD, PPORRES) |>
  arrange(Subject, PPTESTCD)
# A tibble: 36 × 3
   Subject PPTESTCD   PPORRES
   <ord>   <chr>        <dbl>
 1 1       cl.last      14.5 
 2 1       cl.obs       12.3 
 3 1       cl.pred      12.3 
 4 1       vss.iv.obs   47.2 
 5 1       vss.obs      47.2 
 6 1       vz.obs       77.6 
 7 4       cl.last      10.2 
 8 4       cl.obs        9.59
 9 4       cl.pred       9.90
10 4       vss.iv.obs   22.4 
# ℹ 26 more rows

7.3.6 Mean Residence Time (MRT)

MRT quantifies the average time a drug molecule spends in the body.

For IV, the AUMC (area under the first moment curve) method gives:

All MRT variants for IV:

Parameter Meaning
mrt.iv.last MRT using AUMClast and AUClast (IV correction)
mrt.iv.obs MRT extrapolated to ∞, observed Clast (IV correction)
mrt.iv.pred MRT extrapolated to ∞, predicted Clast (IV correction)
mrt.last MRT using AUMClast (no IV correction)
mrt.obs MRT extrapolated to ∞, observed Clast (no IV correction)
mrt.pred MRT extrapolated to ∞, predicted Clast (no IV correction)

The IV-corrected variants (mrt.iv.*) subtract half the infusion duration from the raw MRT: MRT_IV = AUMC/AUC − duration/2. For an IV bolus (duration = 0) the correction is zero; for a 0.5 h infusion the correction is 0.25 h. The non-IV variants (mrt.*) use the raw AUMC/AUC ratio without any duration correction.

mrt_params <- data.frame(
  start       = first_t,
  end         = Inf,
  mrt.iv.last = TRUE,
  mrt.iv.obs  = TRUE,
  mrt.iv.pred = TRUE,
  mrt.last    = TRUE,
  mrt.obs     = TRUE,
  mrt.pred    = TRUE
)

o_data_mrt <- PKNCAdata(o_conc, o_dose, intervals = mrt_params, impute = "start_conc0")
o_nca_mrt  <- pk.nca(o_data_mrt)

as.data.frame(o_nca_mrt) |>
  filter(grepl("^mrt", PPTESTCD)) |>
  select(Subject, PPTESTCD, PPORRES) |>
  arrange(Subject, PPTESTCD)
# A tibble: 36 × 3
   Subject PPTESTCD    PPORRES
   <ord>   <chr>         <dbl>
 1 1       mrt.iv.last    1.92
 2 1       mrt.iv.obs     3.84
 3 1       mrt.iv.pred    3.85
 4 1       mrt.last       1.92
 5 1       mrt.obs        3.84
 6 1       mrt.pred       3.85
 7 4       mrt.iv.last    1.80
 8 4       mrt.iv.obs     2.33
 9 4       mrt.iv.pred    2.07
10 4       mrt.last       1.80
# ℹ 26 more rows

7.3.7 Effective half-life and elimination rate constant

Effective half-life (thalf.eff) is derived from MRT rather than from the terminal regression: thalf.eff = ln(2) × MRT. It reflects the half-life that would produce the same accumulation as the observed MRT, useful for multiple-dose design.

kel in PKNCA is computed as 1 / MRT, not as λz (the terminal slope from the half-life regression). kel and lambda.z answer different questions: lambda.z is the terminal log-linear slope; kel is the MRT-derived rate constant used for multiple-dose accumulation calculations.

All variants:

Parameter Formula Based on
thalf.eff.iv.last ln(2) × MRT mrt.iv.last
thalf.eff.iv.obs ln(2) × MRT mrt.iv.obs
thalf.eff.iv.pred ln(2) × MRT mrt.iv.pred
thalf.eff.last ln(2) × MRT mrt.last
thalf.eff.obs ln(2) × MRT mrt.obs
kel.obs 1 / MRT mrt.obs
kel.pred 1 / MRT mrt.pred
kel.last 1 / MRT mrt.last
kel.iv.obs 1 / MRT mrt.iv.obs
kel.iv.pred 1 / MRT mrt.iv.pred
kel.iv.last 1 / MRT mrt.iv.last
eff_params <- data.frame(
  start              = first_t,
  end                = Inf,
  thalf.eff.iv.last  = TRUE,
  thalf.eff.iv.obs   = TRUE,
  thalf.eff.iv.pred  = TRUE,
  thalf.eff.last     = TRUE,
  thalf.eff.obs      = TRUE,
  kel.obs            = TRUE,
  kel.pred           = TRUE,
  kel.last           = TRUE,
  kel.iv.obs         = TRUE,
  kel.iv.pred        = TRUE,
  kel.iv.last        = TRUE
)

o_nca_eff <- pk.nca(PKNCAdata(o_conc, o_dose, intervals = eff_params, impute = "start_conc0"))

as.data.frame(o_nca_eff) |>
  filter(grepl("^(thalf|kel)", PPTESTCD)) |>
  select(Subject, PPTESTCD, PPORRES) |>
  arrange(Subject, PPTESTCD)
# A tibble: 66 × 3
   Subject PPTESTCD          PPORRES
   <ord>   <chr>               <dbl>
 1 1       kel.iv.last         0.522
 2 1       kel.iv.obs          0.260
 3 1       kel.iv.pred         0.260
 4 1       kel.last            0.522
 5 1       kel.obs             0.260
 6 1       kel.pred            0.260
 7 1       thalf.eff.iv.last   1.33 
 8 1       thalf.eff.iv.obs    2.66 
 9 1       thalf.eff.iv.pred   2.67 
10 1       thalf.eff.last      1.33 
# ℹ 56 more rows

7.3.8 IV-route AUC (auciv*)

auciv* parameters compute AUC using IV-specific formulas. Unlike the general auclast/aucinf.obs, these treat the dose as a bolus at time 0 and back-extrapolate C0 before integrating.

Complete auciv* family:

Parameter Meaning
aucivlast AUC(0–last), IV method (C0 back-extrapolated)
aucivall AUC(0–last), IV method, BLQ treated as 0
aucivinf.obs AUC(0–∞) IV method, observed Clast
aucivinf.pred AUC(0–∞) IV method, predicted Clast
aucivint.last AUC over interval [start,end], IV method, AUClast extrapolation
aucivint.all AUC over interval [start,end], IV method, AUCall extrapolation
aucivpbextlast % of aucivinf.obs beyond last sample (IV, last type)
aucivpbextall % of aucivinf.obs beyond last sample (IV, all type)
aucivpbextinf.obs % of aucivinf.obs that is extrapolated beyond the last sample
aucivpbextinf.pred Same using predicted Clast
aucivpbextint.last % extrapolated for interval-based auciv (last type)
aucivpbextint.all % extrapolated for interval-based auciv (all type)
auciv_params <- data.frame(
  start              = first_t,
  end                = Inf,
  aucivlast          = TRUE,
  aucivall           = TRUE,
  aucivinf.obs       = TRUE,
  aucivinf.pred      = TRUE,
  aucivpbextlast     = TRUE,
  aucivpbextall      = TRUE,
  aucivpbextinf.obs  = TRUE,
  aucivpbextinf.pred = TRUE
)

o_nca_auciv <- pk.nca(PKNCAdata(o_conc, o_dose, intervals = auciv_params, impute = "start_conc0"))

as.data.frame(o_nca_auciv) |>
  filter(grepl("^auciv", PPTESTCD)) |>
  select(Subject, PPTESTCD, PPORRES) |>
  arrange(Subject, PPTESTCD)
# A tibble: 48 × 3
   Subject PPTESTCD           PPORRES
   <ord>   <chr>                <dbl>
 1 1       aucivall              2.01
 2 1       aucivinf.obs          2.33
 3 1       aucivinf.pred         2.33
 4 1       aucivlast             2.01
 5 1       aucivpbextall        14.5 
 6 1       aucivpbextinf.obs    12.5 
 7 1       aucivpbextinf.pred   12.5 
 8 1       aucivpbextlast       14.5 
 9 4       aucivall              2.75
10 4       aucivinf.obs          2.91
# ℹ 38 more rows

When to use auciv* vs auclast/aucinf.obs: For a true IV bolus where C0 is back-extrapolated, aucivlast and aucivinf.obs give the more theoretically correct AUC. For practical purposes the numerical difference is usually small.


7.4 IV infusion (non-bolus)

For IV infusions, tell PKNCA the infusion duration via a duration column in the dose data frame and the duration argument to PKNCAdose(). PKNCA then:

  • Interpolates the concentration at end of infusion (ceoi) from the surrounding measured timepoints
  • Applies the IV correction to MRT: MRT_infusion = AUMC/AUC − duration/2

The Indomethacin dataset is IV bolus, but we can re-label the dose as a 30-minute (0.5 h) infusion to illustrate the workflow. The concentration data are unchanged; only the dose metadata differs.

# Re-label the bolus dose as a 30-minute infusion
d_dose_inf <- d_dose |>
  mutate(duration = 0.5)  # 0.5 hours = 30 min

o_dose_inf <- PKNCAdose(
  d_dose_inf,
  dose ~ time | Subject,
  route    = "intravascular",
  duration = "duration"   # column name in d_dose_inf
)
Found column named route, using it for the attribute of the same name.

7.4.1 Infusion-specific parameter: ceoi

ceoi is the concentration at the end of infusion — the PK analogue of Cmax for extravascular routes. PKNCA interpolates it from the surrounding observed timepoints using the current AUC method.

inf_intervals <- data.frame(
  start      = 0,
  end        = Inf,
  ceoi       = TRUE,    # concentration at end of infusion (interpolated)
  auclast    = TRUE,
  aucinf.obs = TRUE,
  half.life  = TRUE,
  cl.obs     = TRUE,
  mrt.iv.obs = TRUE     # MRT with infusion duration correction
)

o_data_inf <- PKNCAdata(o_conc, o_dose_inf, intervals = inf_intervals,
                         impute = "start_conc0")
o_nca_inf  <- pk.nca(o_data_inf)

as.data.frame(o_nca_inf) |>
  filter(PPTESTCD %in% c("ceoi", "auclast", "aucinf.obs",
                          "half.life", "cl.obs", "mrt.iv.obs")) |>
  select(Subject, PPTESTCD, PPORRES) |>
  tidyr::pivot_wider(names_from = PPTESTCD, values_from = PPORRES) |>
  arrange(Subject)
# A tibble: 6 × 7
  Subject auclast  ceoi half.life aucinf.obs cl.obs mrt.iv.obs
  <ord>     <dbl> <dbl>     <dbl>      <dbl>  <dbl>      <dbl>
1 1          1.72  0.94      4.38       2.04  12.3        3.59
2 4          2.44  1.39      1.73       2.62   9.55       2.13
3 2          2.89  1.63      2.29       3.15   7.93       2.73
4 5          1.92  1.04      2.74       2.16  11.6        2.79
5 6          2.84  1.44      1.96       3.10   8.08       2.44
6 3          2.88  1.49      1.75       3.08   8.11       2.07

7.4.2 MRT correction for infusions

The mrt.iv.* parameters subtract duration/2 from the raw AUMC/AUC ratio:

\[\text{MRT}_{IV} = \frac{\text{AUMC}}{\text{AUC}} - \frac{\text{duration}}{2}\]

For an IV bolus (duration = 0) the correction is zero. For a 0.5 h infusion, the correction is 0.25 h. When early samples fall within the infusion window the correction may differ slightly from the nominal duration/2 due to the way PKNCA handles within-infusion timepoints in the AUMC calculation.

Use mrt.iv.obs (extrapolated to ∞) when the terminal phase is well characterised and mrt.iv.last (to last measurable) otherwise.


7.5 AUC over a specific interval

For steady-state or partial AUC windows:

# AUC from first sample to 4 hours
partial_auc <- data.frame(
  start   = first_t,
  end     = 4,
  auclast = TRUE,
  cmax    = TRUE
)

o_data_partial <- PKNCAdata(o_conc, o_dose, intervals = partial_auc, impute = "start_conc0")
o_nca_partial  <- pk.nca(o_data_partial)

as.data.frame(o_nca_partial) |>
  select(Subject, PPTESTCD, PPORRES) |>
  arrange(Subject)
# A tibble: 12 × 3
   Subject PPTESTCD PPORRES
   <ord>   <chr>      <dbl>
 1 1       auclast     1.43
 2 1       cmax        1.5 
 3 4       auclast     2.12
 4 4       cmax        1.85
 5 2       auclast     2.29
 6 2       cmax        2.03
 7 5       auclast     1.58
 8 5       cmax        2.05
 9 6       auclast     2.39
10 6       cmax        2.31
11 3       auclast     2.51
12 3       cmax        2.72

7.6 Units

Assign units to get automatic unit propagation through derived parameters:

units_table <- pknca_units_table(
  concu  = "mcg/mL",
  timeu  = "h",
  doseu  = "mg",
  amountu = "mg"
)

o_conc_u <- PKNCAconc(d_conc, conc ~ time | Subject, concu = "mcg/mL", timeu = "h")
o_dose_u <- PKNCAdose(d_dose, dose ~ time | Subject, route = "intravascular",
                      doseu = "mg")
Found column named route, using it for the attribute of the same name.
o_data_u <- PKNCAdata(o_conc_u, o_dose_u, intervals = iv_intervals, units = units_table,
                     impute = "start_conc0")
o_nca_u  <- pk.nca(o_data_u)

as.data.frame(o_nca_u) |>
  filter(PPTESTCD %in% c("auclast", "cl.obs", "vz.obs")) |>
  select(Subject, PPTESTCD, PPORRES) |>
  arrange(Subject, PPTESTCD)
# A tibble: 18 × 3
   Subject PPTESTCD PPORRES
   <ord>   <chr>      <dbl>
 1 1       auclast     1.72
 2 1       cl.obs     12.3 
 3 1       vz.obs     77.6 
 4 4       auclast     2.44
 5 4       cl.obs      9.59
 6 4       vz.obs     22.3 
 7 2       auclast     2.89
 8 2       cl.obs      7.93
 9 2       vz.obs     26.2 
10 5       auclast     1.92
11 5       cl.obs     11.6 
12 5       vz.obs     45.8 
13 6       auclast     2.84
14 6       cl.obs      8.08
15 6       vz.obs     22.8 
16 3       auclast     2.88
17 3       cl.obs      8.14
18 3       vz.obs     19.3 

7.7 Excluding data points

Mark individual observations to exclude from calculations (e.g., suspected sampling errors):

d_conc_with_excl <- d_conc |>
  mutate(excl = ifelse(Subject == "2" & time == 2, "suspected contamination", NA_character_))

o_conc_excl <- PKNCAconc(d_conc_with_excl, conc ~ time | Subject, exclude = "excl")

Excluded points are tracked in the results and appear in as.data.frame() output.


7.8 Summary with custom statistics

# Default summary uses geometric mean ± CV% for most parameters
summary(o_nca)
 start end N     auclast        cmax                 tmax mrt.iv.last
     0 Inf 6 2.40 [23.0] 2.04 [20.3] 0.250 [0.250, 0.250] 1.93 [8.32]
   half.life     lambda.z  aucinf.obs      cl.obs  mrt.iv.obs      vz.obs
 2.44 [1.04] 0.303 [39.0] 2.65 [19.6] 9.45 [19.6] 2.82 [19.6] 31.2 [57.9]

Caption: auclast, cmax, mrt.iv.last, lambda.z, aucinf.obs, cl.obs, mrt.iv.obs, vz.obs: geometric mean and geometric coefficient of variation; tmax: median and range; half.life: arithmetic mean and standard deviation; N: number of subjects

Customize the summary function (e.g., arithmetic mean ± SD for Tmax):

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)
 start end N     auclast        cmax                 tmax mrt.iv.last
     0 Inf 6 2.40 [23.0] 2.04 [20.3] 0.250 [0.250, 0.250] 1.93 [8.32]
   half.life     lambda.z  aucinf.obs      cl.obs  mrt.iv.obs      vz.obs
 2.44 [1.04] 0.303 [39.0] 2.65 [19.6] 9.45 [19.6] 2.82 [19.6] 31.2 [57.9]

Caption: auclast, cmax, mrt.iv.last, lambda.z, aucinf.obs, cl.obs, mrt.iv.obs, vz.obs: geometric mean and geometric coefficient of variation; tmax: median [min, max]; half.life: arithmetic mean and standard deviation; N: number of subjects

7.9 Full parameter list for IV

All IV-relevant parameters you can request in an interval:

interval_cols <- get.interval.cols()
iv_relevant <- c(
  # Concentration / time landmarks
  "c0", "cmax", "cmin", "tmax", "tfirst", "tlast",
  "clast.obs", "clast.pred", "count_conc", "count_conc_measured",
  # AUC family
  "auclast", "aucall", "aucinf.obs", "aucinf.pred",
  "aucpext.obs", "aucpext.pred",
  "aucint.last", "aucint.last.dose", "aucint.all", "aucint.all.dose",
  "aucint.inf.obs", "aucint.inf.obs.dose",
  # IV-specific AUC
  "aucivlast", "aucivall", "aucivinf.obs", "aucivinf.pred",
  "aucivint.last", "aucivint.all",
  "aucivpbextlast", "aucivpbextall", "aucivpbextinf.obs", "aucivpbextinf.pred",
  # AUMC
  "aumclast", "aumcall", "aumcinf.obs", "aumcinf.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
  "cl.obs", "cl.pred", "cl.last", "cl.all",
  # Volume
  "vz.obs", "vz.pred", "vss.obs", "vss.pred", "vss.iv.obs", "vss.iv.pred",
  "vss.last", "vss.iv.last",
  # MRT
  "mrt.iv.last", "mrt.iv.obs", "mrt.iv.pred", "mrt.last", "mrt.obs", "mrt.pred",
  # Effective half-life / kel
  "thalf.eff.iv.last", "thalf.eff.iv.obs", "thalf.eff.iv.pred",
  "thalf.eff.last", "thalf.eff.obs",
  "kel.obs", "kel.pred", "kel.last", "kel.iv.obs", "kel.iv.pred", "kel.iv.last",
  # Dose-normalized
  "auclast.dn", "aucinf.obs.dn", "cmax.dn", "clast.obs.dn"
)

# All registered in PKNCA
sort(names(interval_cols)[names(interval_cols) %in% iv_relevant])
 [1] "adj.r.squared"       "aucall"              "aucinf.obs"         
 [4] "aucinf.obs.dn"       "aucinf.pred"         "aucint.all"         
 [7] "aucint.all.dose"     "aucint.inf.obs"      "aucint.inf.obs.dose"
[10] "aucint.last"         "aucint.last.dose"    "aucivall"           
[13] "aucivinf.obs"        "aucivinf.pred"       "aucivint.all"       
[16] "aucivint.last"       "aucivlast"           "aucivpbextall"      
[19] "aucivpbextinf.obs"   "aucivpbextinf.pred"  "aucivpbextlast"     
[22] "auclast"             "auclast.dn"          "aucpext.obs"        
[25] "aucpext.pred"        "aumcall"             "aumcinf.obs"        
[28] "aumcinf.pred"        "aumclast"            "c0"                 
[31] "cl.all"              "cl.last"             "cl.obs"             
[34] "cl.pred"             "clast.obs"           "clast.obs.dn"       
[37] "clast.pred"          "cmax"                "cmax.dn"            
[40] "cmin"                "count_conc"          "count_conc_measured"
[43] "half.life"           "kel.iv.last"         "kel.iv.obs"         
[46] "kel.iv.pred"         "kel.last"            "kel.obs"            
[49] "kel.pred"            "lambda.z"            "lambda.z.n.points"  
[52] "lambda.z.time.first" "lambda.z.time.last"  "mrt.iv.last"        
[55] "mrt.iv.obs"          "mrt.iv.pred"         "mrt.last"           
[58] "mrt.obs"             "mrt.pred"            "r.squared"          
[61] "span.ratio"          "tfirst"              "thalf.eff.iv.last"  
[64] "thalf.eff.iv.obs"    "thalf.eff.iv.pred"   "thalf.eff.last"     
[67] "thalf.eff.obs"       "tlast"               "tmax"               
[70] "vss.iv.last"         "vss.iv.obs"          "vss.iv.pred"        
[73] "vss.last"            "vss.obs"             "vss.pred"           
[76] "vz.obs"              "vz.pred"