3  Interval Selection and Parameter Catalog

Intervals tell PKNCA when to calculate and what to calculate. Every calculated parameter value comes from exactly one interval row, and each row is independent of all others. See also the package vignette Selection of Calculation Intervals.


3.1 The interval data frame structure

An interval specification is a plain R data.frame with one row per calculation window. Two columns are required:

Column Type Meaning
start numeric Start of the calculation window (same units as time)
end numeric End of the calculation window; use Inf for open-ended windows

Every remaining column is normally a parameter flag: its name must match a known PKNCA parameter name and its value must be TRUE (calculate) or FALSE (skip). Columns set to FALSE are simply ignored, so you only need to list the parameters you actually want. The exceptions are:

  • grouping columns used for matching (see the group-matching section below);
  • the imputation column, which may have any name and is the column that the impute argument of PKNCAdata() names (see Imputation columns below and the Concentration Imputation chapter);
  • an interval_id column and <parameter>_ref columns, which link an interval to a reference interval for secondary parameters such as renal clearance, bioavailability, and ratios (≥ 0.13.0; see the Secondary Parameters chapter); and
  • value columns that some parameters read from the interval: conc_above (the concentration threshold for time_above) and tau (the dosing interval for mrt.md.* and vss.md.*, ≥ 0.13.0; see the Multiple-Dose and Steady-State chapter);
  • interval_time_kind, which pk.nca() adds when it converts date-time intervals; and
  • columns carried through to the results via the keep_interval_cols option.

Any other column is an error (pknca_error_invalid_interval_columns) when the intervals are checked by PKNCAdata(intervals = ), set_intervals(), or pk.nca() (≥ 0.13.0; see Validating intervals).

# A minimal interval: AUClast over the first 24 hours
my_intervals <- data.frame(
  start   = 0,
  end     = 24,
  auclast = TRUE
)
my_intervals
  start end auclast
1     0  24    TRUE

You can also request multiple parameters in a single row:

data.frame(
  start      = 0,
  end        = Inf,
  cmax       = TRUE,
  tmax       = TRUE,
  half.life  = TRUE,
  aucinf.obs = TRUE
)
  start end cmax tmax half.life aucinf.obs
1     0 Inf TRUE TRUE      TRUE       TRUE

A parameter cannot be named start, end, or the same as a grouping variable you use in PKNCAconc() — a column whose name matches a grouping variable is treated as a matching key, not a parameter flag.


3.2 Group matching

PKNCA matches interval rows to concentration data by comparing the shared column names between the interval data frame and the grouping columns of the concentration object. Any column that appears in both is used as a matching key.

o_conc <- PKNCAconc(Theoph, conc ~ Time | Subject)

# Intervals with a Subject column — each row applies to exactly one subject
subject_intervals <- data.frame(
  Subject = c("1", "2"),
  start   = c(0, 0),
  end     = c(24, 24),
  auclast = TRUE
)

# Intervals without Subject — this row applies to ALL subjects (default interval)
# Shown for illustration; the run below uses subject_intervals only
default_interval <- data.frame(
  start   = 0,
  end     = 24,
  auclast = TRUE
)

If an interval row has no group columns, it is a default interval and is applied to every group present in the concentration data. This is useful when the same calculation windows apply to all subjects and analytes.

Running the subject-specific intervals shows the matching in action. To keep the output short, restrict the concentration data to subjects 1–3. Subject 3 has no matching interval row, so PKNCA warns about it and returns results only for subjects 1 and 2 (no dose object is needed here because auclast is dose-independent):

o_conc_subset <- PKNCAconc(
  Theoph |> filter(Subject %in% c("1", "2", "3")),
  conc ~ Time | Subject
)
o_data_subj <- PKNCAdata(o_conc_subset, intervals = subject_intervals)
o_nca_subj  <- pk.nca(o_data_subj)
Warning: Subject=3: No intervals for data
as.data.frame(o_nca_subj) |>
  select(Subject, start, end, PPTESTCD, PPORRES)
# A tibble: 2 × 5
  Subject start   end PPTESTCD PPORRES
  <chr>   <dbl> <dbl> <chr>      <dbl>
1 2           0    24 auclast     67.2
2 1           0    24 auclast     92.4

3.3 Default intervals with no intervals argument

When you call PKNCAdata() without an intervals argument, PKNCA generates intervals from the dose times of each group. It finds the dosing regimen, places the intervals (after a single dose, after the first dose, over the last dosing interval at steady state), and asks pknca_interval_table() (described below) for the parameters and the imputation that suit each interval (≥ 0.13.0). For a single extravascular dose, as in Theoph, every subject gets one interval from the dose to infinity:

o_dose <- PKNCAdose(
  Theoph |> group_by(Subject) |>
    summarise(Dose = Dose[1] * Wt[1], .groups = "drop") |>
    mutate(Time = 0),
  Dose ~ Time | Subject
)

# No intervals argument: intervals are generated automatically
o_data_default <- PKNCAdata(
  PKNCAconc(Theoph, conc ~ Time | Subject),
  o_dose
)

# Show subject 1, keeping only the columns that request a parameter
o_data_default$intervals |>
  filter(Subject == "1") |>
  select(where(~ !is.logical(.x) || any(.x)))
# A tibble: 1 × 13
  start   end auclast cmax  tmax  tlag  count_conc half.life aucinf.obs
  <dbl> <dbl> <lgl>   <lgl> <lgl> <lgl> <lgl>      <lgl>     <lgl>     
1     0   Inf TRUE    TRUE  TRUE  TRUE  TRUE       TRUE      TRUE      
# ℹ 4 more variables: aucpext.obs <lgl>, cl.obs <lgl>, Subject <ord>,
#   impute <chr>

The row requests auclast, cmax, tmax, tlag, count_conc, half.life, aucinf.obs, aucpext.obs, and cl.obs, and its impute column holds start_predose_conc0, which keeps a concentration measured at the dose time, carries a predose concentration forward when there is none, and imputes 0 otherwise. An intravenous bolus gets an extra row that back-extrapolates c0 without imputation, multiple-dose data get rows built on the aucint.* family, and a sparse design imputes only a zero at a single dose. The Automatic Intervals and Dosing Regimens chapter covers how the regimen is detected, the auto.interval.tolerance and tau.choices options, and the intervals each design gets.

NoteChanged in 0.13.0

Before 0.13.0, a single dose got the two rows of the single.dose.aucs option instead: auclast over 0 to 24 and cmax, tmax, half.life, and aucinf.obs over 0 to infinity, with no imputation. The option still exists and is used when auto.interval.method = "legacy":

o_data_legacy <- PKNCAdata(
  PKNCAconc(Theoph, conc ~ Time | Subject),
  o_dose,
  options = list(auto.interval.method = "legacy")
)
o_data_legacy$intervals |>
  filter(Subject == "1") |>
  select(where(~ !is.logical(.x) || any(.x)))
# A tibble: 2 × 8
  start   end auclast cmax  tmax  half.life aucinf.obs Subject
  <dbl> <dbl> <lgl>   <lgl> <lgl> <lgl>     <lgl>      <ord>  
1     0    24 TRUE    FALSE FALSE FALSE     FALSE      1      
2     0   Inf FALSE   TRUE  TRUE  TRUE      TRUE       1      

The 0 to 24 window of single.dose.aucs is written for hours; with a time unit other than hours, the legacy method warns (pknca_warning_single_dose_aucs_unit). See also Options for Controlling PKNCA.


3.4 Multiple intervals per group

Each row of the interval data frame is applied independently. A single subject (or group) can have any number of interval rows, each with its own time window and parameter set. PKNCA applies every matching row and collects the results.

# Two windows for the same subjects:
# 0–24 h for early exposure; 0–Inf for terminal parameters
multi_intervals <- data.frame(
  start      = c(0, 0),
  end        = c(24, Inf),
  auclast    = c(TRUE,  FALSE),
  cmax       = c(FALSE, TRUE),
  half.life  = c(FALSE, TRUE),
  aucinf.obs = c(FALSE, TRUE)
)
multi_intervals
  start end auclast  cmax half.life aucinf.obs
1     0  24    TRUE FALSE     FALSE      FALSE
2     0 Inf   FALSE  TRUE      TRUE       TRUE

PKNCA processes each row separately: the first row restricts data to \(t \in [0, 24]\) for the auclast calculation, while the second row uses all available data to derive terminal parameters.

Overlapping intervals are allowed and do not interfere with each other.


3.5 Duration-based events

Some sampling designs record a duration rather than an instantaneous sample — for example, urine collection periods or IV infusions. PKNCA tracks these via the duration argument in PKNCAconc() and PKNCAdose().

When a concentration object carries duration information, PKNCA still selects samples for an interval by their recorded time, which is the start of the collection period: a collection is included when its start is at or after the interval start and at or before the interval end. The duration is not consulted during selection, so a collection that starts inside the interval but ends after end contributes its full amount to the interval. For results that are simple to interpret, align the collection boundaries with the interval boundaries, as here:

# Urine collected 0-4, 4-8, and 8-24 h after a 100 mg oral dose
d_urine_conc <- data.frame(
  Subject  = 1,
  time     = c(0, 4, 8),        # collection start times (h)
  duration = c(4, 4, 16),       # collection lengths (h)
  conc     = c(20, 15, 5),      # urine concentration (mg/L)
  volume   = c(0.5, 0.6, 1.2)   # collected urine volume (L)
)
o_urine_conc <- PKNCAconc(
  d_urine_conc, conc ~ time | Subject,
  duration = "duration", volume = "volume"
)
o_urine_dose <- PKNCAdose(
  data.frame(Subject = 1, dose = 100, time = 0),
  dose ~ time | Subject
)
# end = 24 matches the end of the final collection (8 + 16 = 24)
urine_intervals <- data.frame(start = 0, end = 24, ae = TRUE)

o_urine <- pk.nca(PKNCAdata(o_urine_conc, o_urine_dose, intervals = urine_intervals))
as.data.frame(o_urine)
# A tibble: 1 × 7
  Subject start   end PPTESTCD PPORRES PPANMETH exclude
    <dbl> <dbl> <dbl> <chr>      <dbl> <chr>    <chr>  
1       1     0    24 ae            25 ""       <NA>   

ae (amount excreted) is the sum of concentration × volume over the collections in the interval: \(20 \times 0.5 + 15 \times 0.6 + 5 \times 1.2 = 25\) mg of the 100 mg dose.

For IV infusions, setDuration() on the dose object sets the infusion duration, and ceoi (concentration at end of infusion) becomes calculable within an interval when a sample was drawn at the end-of-infusion time within the interval.


3.6 Building intervals with pknca_interval_table()

pknca_interval_table() (≥ 0.13.0) builds an interval specification from a description of the analysis instead of a list of parameter names. Given a start, an end, how often the drug was given (dosing), and how (route), it chooses the parameters usually reported for that context, the AUC they are built on, and the imputation to calculate them from. It is the function the automatic intervals use, so for a single extravascular dose it returns the same row as the default intervals above:

pknca_interval_table(start = 0, end = Inf, dosing = "single", route = "extravascular") |>
  select(where(~ !is.logical(.x) || any(.x)))
  start end auclast cmax tmax tlag count_conc half.life aucinf.obs aucpext.obs
1     0 Inf    TRUE TRUE TRUE TRUE       TRUE      TRUE       TRUE        TRUE
  cl.obs              impute
1   TRUE start_predose_conc0

The arguments describe the context:

Argument Values Meaning
start, end numbers (vectors allowed) The interval bounds; vectors give one set of rows per interval
dosing "single" (default), "multiple", "steady_state" Given once, repeatedly, or repeatedly at steady state (pknca_dosing())
route "extravascular" (default), "iv_bolus", "iv_infusion", "iv_continuous_infusion" How the drug was given (pknca_routes()); note these are not the "intravascular" spelling of PKNCAdose()
sample_type "spot" (default), "interval" Samples at a point in time (blood, plasma) or collections over an interval (urine, feces) (pknca_sample_types())
sparse FALSE (default), TRUE A sparse design keeps the same parameters but imputes only start_conc0 at a single dose and nothing otherwise
tier "common" (default), "all" The parameters usually reported, or every parameter the context can calculate
include, exclude parameter names or concepts Add to or remove from what the context gives; naming one in both is an error
impute NULL (default), a method string, or NA NULL chooses the imputation from the context; NA uses none
preset a name from pknca_presets() A named set of the arguments above to start from; explicit arguments override it
clast_type "obs" (default), "pred" Extrapolate to infinity from the observed or the predicted last concentration
... named values Columns added to every row, typically the groups the interval applies to

A single dose is built on AUClast and its extrapolation to infinity; a repeated dose is built on the aucint.* family, which interpolates at both interval boundaries, with clearance, volume, and MRT chosen to match. At steady state with the fluctuation parameters added (and a group column given through ...):

pknca_interval_table(
  start = 144, end = 168,
  dosing = "steady_state", route = "extravascular",
  include = "fluctuation",
  Subject = "1"
) |>
  select(where(~ !is.logical(.x) || any(.x)))
  start end aucint.last cmax tmax ctrough  ptr tlag deg.fluc swing count_conc
1   144 168        TRUE TRUE TRUE    TRUE TRUE TRUE     TRUE  TRUE       TRUE
  half.life aucint.inf.obs cl.int.inf.obs        impute Subject
1      TRUE           TRUE           TRUE start_predose       1

include and exclude accept parameter names or the concepts listed by pknca_concepts(), so include = "mrt" adds the MRT parameters the single-dose context calculates (mrt.last and mrt.obs in the output below) and exclude = "clearance" drops cl.obs:

pknca_interval_table(0, Inf, include = c("mrt", "aumclast"), exclude = "clearance") |>
  select(where(~ !is.logical(.x) || any(.x)))
  start end auclast aumclast cmax tmax mrt.last tlag count_conc half.life
1     0 Inf    TRUE     TRUE TRUE TRUE     TRUE TRUE       TRUE      TRUE
  aucinf.obs aucpext.obs mrt.obs              impute
1       TRUE        TRUE    TRUE start_predose_conc0

When an imputed value would become the answer, the parameter is kept out of the imputation by splitting the interval into more than one row. After an intravenous bolus, c0 is back-extrapolated in a second row with no imputation, so that an imputed concentration at the dose time cannot become C0:

pknca_interval_table(0, Inf, dosing = "single", route = "iv_bolus") |>
  select(where(~ !is.logical(.x) || any(.x)))
  start end auclast    c0  cmax  tmax count_conc half.life aucinf.obs
1     0 Inf    TRUE FALSE  TRUE  TRUE       TRUE      TRUE       TRUE
2     0 Inf   FALSE  TRUE FALSE FALSE      FALSE     FALSE      FALSE
  aucpext.obs cl.obs              impute
1        TRUE   TRUE start_predose_conc0
2       FALSE  FALSE                <NA>

tier = "all" gives every parameter the context can calculate (55 parameters for a single extravascular dose, rather than 9), which is a convenient starting point that you can then trim with interval_remove_param().

The table is an ordinary interval data frame, so it goes straight into PKNCAdata(). Its impute column is read because impute defaults to NA, which looks for a column named "impute":

o_conc <- PKNCAconc(Theoph, conc ~ Time | Subject)
d_int_table <- pknca_interval_table(0, Inf, include = "aumclast")
o_nca_table <- pk.nca(PKNCAdata(o_conc, o_dose, intervals = d_int_table))
as.data.frame(o_nca_table) |>
  filter(Subject == "1") |>
  select(Subject, start, end, PPTESTCD, PPORRES)
# A tibble: 21 × 5
   Subject start   end PPTESTCD     PPORRES
   <ord>   <dbl> <dbl> <chr>          <dbl>
 1 1           0   Inf auclast     147.    
 2 1           0   Inf aumclast   1499.    
 3 1           0   Inf cmax         10.5   
 4 1           0   Inf tmax          1.12  
 5 1           0   Inf tlast        24.4   
 6 1           0   Inf clast.obs     3.28  
 7 1           0   Inf tlag          0     
 8 1           0   Inf count_conc   11     
 9 1           0   Inf lambda.z      0.0485
10 1           0   Inf r.squared     1.000 
# ℹ 11 more rows

3.6.1 Presets

pknca_presets() gives named argument sets for common analyses. Each is a list of arguments to pknca_interval_table():

names(pknca_presets())
[1] "single_dose"        "steady_state"       "bioequivalence"    
[4] "first_in_human"     "mass_balance"       "sparse_single_dose"
pknca_presets()$bioequivalence
$dosing
[1] "single"

$route
[1] "extravascular"

$include
[1] "aucall"    "clast.obs" "tlast"    
Preset Arguments it sets
single_dose dosing = "single", route = "extravascular"
steady_state dosing = "steady_state", route = "extravascular"
bioequivalence single extravascular dose, include = c("aucall", "clast.obs", "tlast")
first_in_human single extravascular dose, include = c("clast.obs", "tlast", "span.ratio")
mass_balance single extravascular dose, sample_type = "interval", include = "excretion_rate"
sparse_single_dose single extravascular dose, sparse = TRUE

Arguments given directly override the preset, here removing the imputation:

pknca_interval_table(0, Inf, preset = "bioequivalence", impute = NA) |>
  select(where(~ !is.logical(.x) || any(.x)))
  start end auclast aucall cmax tmax tlast clast.obs tlag count_conc half.life
1     0 Inf    TRUE   TRUE TRUE TRUE  TRUE      TRUE TRUE       TRUE      TRUE
  aucinf.obs aucpext.obs cl.obs
1       TRUE        TRUE   TRUE

The Bioequivalence chapter uses the bioequivalence preset, and the Sparse Sampling chapter covers sparse designs.


3.7 How parameters are classified with pknca_parameter_table()

pknca_interval_table() chooses parameters from a classification of every registered parameter (≥ 0.13.0). pknca_parameter_table() shows it: the concept a parameter computes, its reporting tier, its sample_type, whether it is a sparse estimator output, secondary (needs inputs from more than one profile), or dose_normalized, and the comma-separated route and dosing contexts it applies to.

pknca_parameter_table(c("auclast", "aucint.last", "cmax", "cl.obs", "ae", "f.obs")) |>
  select(parameter, concept, tier, sample_type, secondary, dosing)
    parameter         concept     tier sample_type secondary
1     auclast             auc   common        spot     FALSE
2 aucint.last             auc   common        spot     FALSE
3        cmax       peak_conc   common        spot     FALSE
4      cl.obs       clearance   common        spot     FALSE
5          ae excreted_amount   common    interval     FALSE
6       f.obs bioavailability uncommon        spot      TRUE
                        dosing
1 single,multiple,steady_state
2 single,multiple,steady_state
3 single,multiple,steady_state
4                       single
5 single,multiple,steady_state
6                       single

The vocabularies have their own listing functions: pknca_concepts(), pknca_tiers(), pknca_dosing(), and pknca_sample_types() (with pknca_routes() for routes).

pknca_concepts()
 [1] "auc"                 "aumc"                "auc_above_conc"     
 [4] "auc_extrapolation"   "peak_conc"           "min_conc"           
 [7] "last_conc"           "average_conc"        "trough_conc"        
[10] "start_conc"          "initial_conc"        "eoi_conc"           
[13] "peak_time"           "min_time"            "last_time"          
[16] "first_time"          "lag_time"            "time_above_conc"    
[19] "clearance"           "renal_clearance"     "volume_z"           
[22] "volume_ss"           "mrt"                 "half_life"          
[25] "effective_half_life" "elimination_rate"    "fluctuation"        
[28] "excreted_amount"     "excreted_fraction"   "excretion_rate"     
[31] "collected_volume"    "bioavailability"     "total_dose"         
[34] "observation_count"   "parameter_ratio"    
pknca_tiers()
[1] "common"   "uncommon"

A parameter’s concept is declared on its calculation function with pknca_concept(), so a parameter registered by another package (or by you, see the Writing Custom Parameter Functions chapter) can be chosen automatically too. An unclassified parameter is not an error: it is calculated when you request it by name but is never selected automatically, and pknca_check_parameter_classification() lists such parameters (zero rows means every registered parameter is classified).

pknca_concept(pk.calc.cmax)
[1] "peak_conc"
nrow(pknca_check_parameter_classification())
[1] 0

Secondary parameters (f.obs, clr.obs, ratio.*, and others) are never selected automatically, because one interval cannot say which other profile is the reference; request them by name as described in the Secondary Parameters chapter.


3.8 Editing intervals

Four functions edit an interval specification (≥ 0.13.0). Each accepts either a data frame of intervals or a PKNCAdata object (whose $intervals it edits), so you can start from the automatic intervals or from pknca_interval_table() and adjust them:

Function What it does Key arguments
interval_add_param() Request more parameters param (names), param_pattern (regular expressions), target_groups
interval_remove_param() Stop requesting parameters param, param_pattern, target_groups
interval_add_impute() Add an imputation method target_impute, after (position in an existing chain: 0 first, Inf last), target_params, target_groups
interval_remove_impute() Remove an imputation method target_impute, target_params, target_groups

target_groups is a data frame of group values; a row of the intervals is edited when it matches every column of at least one of its rows.

d_edit <- data.frame(start = 0, end = Inf, cmax = TRUE, tmax = TRUE)

# Add parameters by name, or by regular expression
interval_add_param(d_edit, param = c("auclast", "half.life")) |>
  select(where(~ !is.logical(.x) || any(.x)))
  start end cmax tmax auclast half.life
1     0 Inf TRUE TRUE    TRUE      TRUE
interval_add_param(d_edit, param_pattern = "^aucint\\.(last|all)$") |>
  select(where(~ !is.logical(.x) || any(.x)))
  start end cmax tmax aucint.last aucint.all
1     0 Inf TRUE TRUE        TRUE       TRUE

Removing every parameter from every interval is an error (pknca_error_interval_nothing_remains), since nothing would be left to calculate.

When the parameters of one row no longer share the same imputation, the row is split into as many rows as needed, and rows that come to share every value are merged again. Imputing a start concentration for auclast only splits the row:

d_edit2 <- data.frame(start = 0, end = Inf, cmax = TRUE, auclast = TRUE, half.life = TRUE)
interval_add_impute(d_edit2, target_impute = "start_conc0", target_params = "auclast") |>
  select(where(~ !is.logical(.x) || any(.x)))
  start end  cmax auclast half.life      impute
1     0 Inf  TRUE   FALSE      TRUE        <NA>
2     0 Inf FALSE    TRUE     FALSE start_conc0

Editing a PKNCAdata object changes all of its intervals at once. Starting from the automatic Theoph intervals, drop tlag and count_conc, add aumclast, and calculate the half-life without the imputed start concentration:

o_data_edited <- o_data_default |>
  interval_remove_param(param = c("tlag", "count_conc")) |>
  interval_add_param(param = "aumclast") |>
  interval_remove_impute(target_impute = "start_predose_conc0", target_params = "half.life")

o_data_edited$intervals |>
  filter(Subject == "1") |>
  select(where(~ !is.logical(.x) || any(.x)))
  start end auclast aumclast  cmax  tmax half.life aucinf.obs aucpext.obs
1     0 Inf    TRUE     TRUE  TRUE  TRUE     FALSE       TRUE        TRUE
2     0 Inf   FALSE    FALSE FALSE FALSE      TRUE      FALSE       FALSE
  cl.obs Subject              impute
1   TRUE       1 start_predose_conc0
2  FALSE       1                <NA>

target_groups restricts an edit to some groups, here subject 2 only:

d_edit3 <- data.frame(Subject = c("1", "2"), start = 0, end = Inf, auclast = TRUE)
interval_add_impute(d_edit3, target_impute = "start_conc0",
                    target_groups = data.frame(Subject = "2"))
  Subject start end auclast      impute
1       1     0 Inf    TRUE        <NA>
2       2     0 Inf    TRUE start_conc0

3.8.1 Imputation columns

The imputation column of an interval table may have any name (≥ 0.13.0). PKNCAdata(impute = ) reads it this way:

  1. If impute names a column of the intervals, each row’s method comes from that column.
  2. Otherwise, if impute is NA (the default) and the intervals have a column named "impute", each row’s method comes from that column. This is how the automatic intervals and pknca_interval_table() carry their imputation.
  3. Otherwise, impute is itself the method string for every interval (an NA with no "impute" column means no imputation).

The column must be character (pknca_error_interval_impute_not_character), except that a column of only NA means no imputation. get_impute_column() and get_impute_method() apply the same rule, which is useful when you write code around PKNCA objects:

d_int_named <- data.frame(
  start = c(0, 0), end = c(24, Inf),
  auclast = c(TRUE, FALSE), aucinf.obs = c(FALSE, TRUE),
  my_impute = c("start_conc0", "start_predose_conc0")
)
o_data_named <- PKNCAdata(o_conc, o_dose, intervals = d_int_named, impute = "my_impute")
get_impute_column(o_data_named$intervals, impute = o_data_named$impute)
[1] "my_impute"
get_impute_method(o_data_named$intervals, impute = o_data_named$impute)
[1] "start_conc0"         "start_predose_conc0"
# A method string is not a column
get_impute_column(d_int_named, impute = "start_conc0")
NULL

interval_add_impute() and interval_remove_impute() edit whichever column the object’s impute setting names. The methods themselves are described in the Concentration Imputation chapter.


3.9 Date-time intervals

With date-time (POSIXct or Date) concentration and dose times (≥ 0.13.0), start and end may also be date-times. pk.nca() converts each row to numeric time relative to the time reference (the first dose) of the group it applies to. A row that does not name every reference group becomes one row per group, because one clock-time window is a different relative window for each subject. An end of Inf stays infinite.

d_conc_dt <- data.frame(
  Subject = rep(c("A", "B"), each = 8),
  datetime = as.POSIXct("2024-03-04 08:00", tz = "UTC") +
    3600 * c(c(0, 0.5, 1, 2, 4, 8, 12, 24), 25.5 + c(0, 0.5, 1, 2, 4, 8, 12, 24)),
  conc = c(0, 4.1, 6.3, 5.9, 4.4, 2.6, 1.5, 0.4,
           0, 3.2, 5.5, 5.8, 4.6, 2.9, 1.8, 0.6)
)
d_dose_dt <- data.frame(
  Subject = c("A", "B"),
  datetime = as.POSIXct(c("2024-03-04 08:00", "2024-03-05 09:30"), tz = "UTC"),
  dose = 100
)
# One clock-time window for both subjects: from subject A's dose onward
d_int_dt <- data.frame(
  start = as.POSIXct("2024-03-04 08:00", tz = "UTC"),
  end = Inf,
  cmax = TRUE
)
o_nca_dt <- pk.nca(PKNCAdata(
  PKNCAconc(d_conc_dt, conc ~ datetime | Subject),
  PKNCAdose(d_dose_dt, dose ~ datetime | Subject),
  intervals = d_int_dt
))
as.data.frame(o_nca_dt) |>
  select(Subject, start, end, PPTESTCD, PPORRES)
# A tibble: 2 × 5
  Subject start   end PPTESTCD PPORRES
  <chr>   <dbl> <dbl> <chr>      <dbl>
1 A         0     Inf cmax         6.3
2 B       -25.5   Inf cmax         5.8

Subject B was dosed 25.5 hours after subject A, so the same window starts at −25.5 h relative to subject B’s dose. The converted intervals, with an interval_time_kind column, are in o_nca_dt$data$intervals. Date-time bounds mixed with finite numeric bounds, an infinite start, date-time intervals for numeric data, and a time zone that differs from the data are errors. The Date-Time Data and Time References chapter covers date-time data in full.


3.10 Full parameter catalog

The complete list of calculable parameters is available programmatically via get.interval.cols(). Each entry carries a description, the calculation function, unit type, and dependency information. The table below joins in the concept and tier from pknca_parameter_table():

cols <- get.interval.cols()

param_table <- data.frame(
  parameter   = names(cols),
  description = sapply(cols, function(x) x$desc),
  stringsAsFactors = FALSE
)

# Exclude the structural columns start/end from the display
param_table <- param_table[!param_table$parameter %in% c("start", "end"), ]
param_table <- merge(
  param_table,
  pknca_parameter_table()[, c("parameter", "concept", "tier")],
  by = "parameter", sort = FALSE
)
rownames(param_table) <- NULL

knitr::kable(
  param_table,
  col.names = c("Parameter", "Description", "Concept", "Tier"),
  caption   = paste0(
    "All ", nrow(param_table),
    " calculable parameters in PKNCA ", packageVersion("PKNCA")
  )
)
All 235 calculable parameters in PKNCA 0.12.1.9002
Parameter Description Concept Tier
auclast AUC start to last conc above LOQ auc common
auclast_se SE of AUClast (sparse PK only) auc uncommon
auclast_df DF for AUClast (sparse PK only) auc uncommon
aucall AUClast plus triangle, 0 at BLQ auc uncommon
aucall_se SE of AUCall (sparse PK only) auc uncommon
aucall_df DF for AUCall (sparse PK only) auc uncommon
aumclast AUMC start to last conc above LOQ aumc uncommon
aumclast_se SE of AUMClast (sparse PK only) aumc uncommon
aumclast_df DF for AUMClast (sparse PK only) aumc uncommon
aumcall AUMClast plus triangle moment, 0 at BLQ aumc uncommon
aumcall_se SE of AUMCall (sparse PK only) aumc uncommon
aumcall_df DF for AUMCall (sparse PK only) aumc uncommon
aucint.last AUC from T1 to T2 (zero extrap) auc common
aucint.all AUC from T1 to T2 (AUCall extrap) auc uncommon
aumcint.last AUMC from T1 to T2 (zero extrap) aumc uncommon
aumcint.all AUMC from T1 to T2 (AUMCall extrap) aumc uncommon
c0 Initial conc after IV bolus initial_conc common
cmax Maximum observed concentration peak_conc common
cmin Minimum observed concentration min_conc uncommon
tmax Time of maximum observed conc peak_time common
tmin Time of minimum observed conc min_time uncommon
tlast Time of last conc above LOQ last_time uncommon
tfirst Time of first conc above LOQ first_time uncommon
clast.obs Last conc observed above LOQ last_conc uncommon
cl.last Clearance, AUClast clearance uncommon
cl.all Clearance, AUCall clearance uncommon
cl.int.all Clearance, AUCint,all clearance uncommon
cl.int.last Clearance, AUCint,last clearance uncommon
mrt.last MRT, AUClast/AUMClast mrt uncommon
mrt.all MRT, AUCall/AUMCall mrt uncommon
mrt.int.all MRT, interval AUCall/AUMCall mrt uncommon
mrt.int.last MRT, interval AUClast/AUMClast mrt uncommon
mrt.iv.last IV MRT, AUClast/AUMClast mrt uncommon
vss.last Vss, calc from AUClast volume_ss uncommon
vss.iv.last IV Vss, calc from AUClast volume_ss uncommon
vss.all Vss, calc from AUCall volume_ss uncommon
vss.int.all Vss, calc from interval AUCint,all volume_ss uncommon
vss.int.last Vss, calc from interval AUCint,last volume_ss uncommon
cav Avg conc in interval (AUClast) average_conc uncommon
cav.int.last Avg conc in interval (AUCint,last) average_conc uncommon
cav.int.all Avg conc in interval (AUCint,all) average_conc uncommon
ctrough Trough (end of interval) conc trough_conc common
cstart The predose concentration start_conc uncommon
ptr Peak-to-trough ratio fluctuation uncommon
tlag Lag time lag_time common
deg.fluc Degree of fluctuation fluctuation uncommon
swing Swing relative to Cmin fluctuation uncommon
ceoi Concentration at the end of infusion eoi_conc common
aucabove.predose.all AUC above predose, floor at 0 auc_above_conc uncommon
aucabove.trough.all AUC above trough, floor at 0 auc_above_conc uncommon
count_conc Count of non-missing conc observation_count common
count_conc_measured Count of measured, non-BLQ conc observation_count uncommon
totdose Total dose given in interval total_dose uncommon
volpk Sum of urine volumes for interval collected_volume common
ae Amount excreted (urine/feces) excreted_amount common
clr.last Renal clearance, AUClast renal_clearance uncommon
clr.obs Renal clearance, AUCinf,obs renal_clearance common
clr.pred Renal clearance, AUCinf,pred renal_clearance uncommon
fe Fraction of dose excreted excreted_fraction common
ertlst Midpoint time of last excr rate excretion_rate uncommon
ermax Maximum excretion rate excretion_rate uncommon
ertmax Midpoint time of max excr rate excretion_rate uncommon
erint Excretion rate from T1 to T2 excretion_rate uncommon
erlst Last measurable excretion rate excretion_rate uncommon
ratio.cmax Ratio of Cmax to reference parameter_ratio uncommon
ratio.auclast Ratio of AUClast to reference parameter_ratio uncommon
ratio.aucint.last Ratio of AUCint,last to reference parameter_ratio uncommon
ratio.aucint.all Ratio of AUCint,all to reference parameter_ratio uncommon
sparse_auclast Sparse AUC to last conc above LOQ auc common
sparse_auc_se SE of sparse AUC to last conc above LOQ auc common
sparse_auc_df DF for sparse AUC to last conc above LOQ auc uncommon
sparse_aumclast Sparse AUMC to last conc above LOQ aumc uncommon
sparse_aumc_se SE of sparse AUMC to last conc above LOQ aumc uncommon
sparse_aumc_df DF for sparse AUMClast aumc uncommon
time_above Time above a given concentration time_above_conc uncommon
aucivlast AUClast, IV back-extrap C0 auc uncommon
aucivlast_se SE of AUClast IV (sparse PK only) auc uncommon
aucivlast_df DF for AUClast IV (sparse PK only) auc uncommon
aucivall AUCall, IV back-extrap C0 auc uncommon
aucivall_se SE of AUCall IV (sparse PK only) auc uncommon
aucivall_df DF for AUCall IV (sparse PK only) auc uncommon
aucivint.last AUCint,last, IV back-extrap C0 auc uncommon
aucivint.all AUCint,all, IV back-extrap C0 auc uncommon
aucivpbextlast Back-extrap %, IV, AUClast auc_extrapolation uncommon
aucivpbextall Back-extrap %, IV, AUCall auc_extrapolation uncommon
aucivpbextint.last Back-extrap %, IV, AUCint,last auc_extrapolation uncommon
aucivpbextint.all Back-extrap %, IV, AUCint,all auc_extrapolation uncommon
aumcivlast AUMClast, IV back-extrap C0 aumc uncommon
aumcivlast_se SE of AUMClast IV (sparse PK only) aumc uncommon
aumcivlast_df DF for AUMClast IV (sparse PK only) aumc uncommon
aumcivall AUMCall, IV back-extrap C0 aumc uncommon
aumcivall_se SE of AUMCall IV (sparse PK only) aumc uncommon
aumcivall_df DF for AUMCall IV (sparse PK only) aumc uncommon
aumcivint.last AUMCint,last, IV back-extrap C0 aumc uncommon
aumcivint.all AUMCint,all, IV back-extrap C0 aumc uncommon
half.life The (terminal) half-life half_life common
r.squared R-squared of half-life fit half_life uncommon
adj.r.squared Adjusted R-sq of half-life fit half_life uncommon
lambda.z.corrxy Corr(time,log-conc) for lambda.z half_life uncommon
lambda.z Terminal elim rate (lambda.z) half_life uncommon
lambda.z.time.first First time point for lambda.z half_life uncommon
lambda.z.time.last Last time point for lambda.z half_life uncommon
lambda.z.n.points Number of points used, lambda.z half_life uncommon
clast.pred Predicted Clast from half-life last_conc uncommon
span.ratio Ratio of lambda.z time span to half-life half_life uncommon
tobit_residual Tobit fit residual SD, log-conc half_life uncommon
adj_tobit_residual Adjusted Tobit residual SD half_life uncommon
lambda.z.n.points_blq BLQ points in Tobit lambda.z half_life uncommon
thalf.eff.last Effective half-life, MRTlast effective_half_life uncommon
thalf.eff.iv.last Effective half-life, IV MRTlast effective_half_life uncommon
kel.last Elim rate, MRT via AUClast elimination_rate uncommon
kel.iv.last Elim rate, IV MRTlast elimination_rate uncommon
kel.all Elim rate, MRTall elimination_rate uncommon
kel.int.all Elim rate, MRTint,all elimination_rate uncommon
kel.int.last Elim rate, MRTint,last elimination_rate uncommon
cl.iv.all IV clearance, AUCall clearance uncommon
cl.iv.last IV clearance, AUClast clearance uncommon
cl.ivint.all IV clearance, AUCint,all clearance uncommon
cl.ivint.last IV clearance, AUCint,last clearance uncommon
cl.sparse.last Clearance, sparse AUClast clearance uncommon
f.last Bioavailability from AUClast bioavailability uncommon
f.int.last Bioavailability from AUCint,last bioavailability uncommon
f.int.all Bioavailability from AUCint,all bioavailability uncommon
mrt.sparse.last MRT, sparse AUClast/AUMClast mrt uncommon
mrt.iv.all IV MRT, AUCall/AUMCall mrt uncommon
mrt.ivint.all IV MRT, interval AUCall/AUMCall mrt uncommon
mrt.ivint.last IV MRT, interval AUClast/AUMClast mrt uncommon
vz.all Vz, AUCall-based CL volume_z uncommon
vz.int.all Vz, interval AUCint,all volume_z uncommon
vz.int.last Vz, interval AUCint,last volume_z uncommon
vz.iv.all IV Vz, AUCall volume_z uncommon
vz.iv.last IV Vz, AUClast volume_z uncommon
vz.ivint.all IV Vz, interval AUCint,all volume_z uncommon
vz.ivint.last IV Vz, interval AUCint,last volume_z uncommon
vz.last Vz, AUClast-based CL volume_z uncommon
vss.iv.all IV Vss, calc from AUCall volume_ss uncommon
vss.ivint.all IV Vss, calc from interval AUCint,all volume_ss uncommon
vss.ivint.last IV Vss, calc from interval AUCint,last volume_ss uncommon
vss.sparse.last Vss, calc from sparse AUClast volume_ss uncommon
aucinf.obs AUC start to inf, obs Clast extrap auc common
aucinf.obs_se SE of AUCinf,obs (sparse PK only) auc uncommon
aucinf.obs_df DF for AUCinf,obs (sparse PK only) auc uncommon
aucinf.pred AUC start to inf, pred Clast extrap auc uncommon
aumcinf.obs AUMC start to inf, obs Clast extrap aumc uncommon
aumcinf.obs_se SE of AUMCinf,obs (sparse PK only) aumc uncommon
aumcinf.obs_df DF for AUMCinf,obs (sparse PK only) aumc uncommon
aumcinf.pred AUMC start to inf, pred Clast extrap aumc uncommon
aucint.inf.obs AUC from T1 to T2 (AUCinf,obs extrap) auc common
aucint.inf.pred AUC from T1 to T2 (AUCinf,pred extrap) auc uncommon
aumcint.inf.obs AUMC from T1 to T2 (AUMCinf,obs extrap) aumc uncommon
aumcint.inf.pred AUMC from T1 to T2 (AUMCinf,pred extrap) aumc uncommon
aucivinf.obs AUCinf,obs, IV back-extrap C0 auc uncommon
aucivinf.obs_se SE of AUCinf,obs IV (sparse PK only) auc uncommon
aucivinf.obs_df DF for AUCinf,obs IV (sparse PK only) auc uncommon
aucivinf.pred AUCinf,pred, IV back-extrap C0 auc uncommon
aucivpbextinf.obs Back-extrap %, IV, AUCinf,obs auc_extrapolation uncommon
aucivpbextinf.pred Back-extrap %, IV, AUCinf,pred auc_extrapolation uncommon
aumcivinf.obs AUMCinf,obs, IV back-extrap C0 aumc uncommon
aumcivinf.obs_se SE of AUMCinf,obs IV (sparse PK only) aumc uncommon
aumcivinf.obs_df DF for AUMCinf,obs IV (sparse PK only) aumc uncommon
aumcivinf.pred AUMCinf,pred, IV back-extrap C0 aumc uncommon
aucpext.obs % AUCinf extrap after Tlast, obs auc_extrapolation common
aucpext.pred % AUCinf extrap after Tlast, pred auc_extrapolation uncommon
kel.iv.all Elim rate, IV MRTall elimination_rate uncommon
kel.ivint.all Elim rate, IV MRTint,all elimination_rate uncommon
kel.ivint.last Elim rate, IV MRTint,last elimination_rate uncommon
kel.sparse.last Elim rate, sparse MRTlast elimination_rate uncommon
cl.obs Clearance, observed Clast clearance common
cl.pred Clearance, predicted Clast clearance uncommon
cl.int.inf.obs Clearance, AUCint,inf,obs clearance common
cl.int.inf.pred Clearance, AUCint,inf,pred clearance uncommon
cl.iv.obs IV clearance, AUCinf,obs clearance uncommon
cl.iv.pred IV clearance, AUCinf,pred clearance uncommon
f.obs Bioavailability from AUCinf,obs bioavailability uncommon
f.pred Bioavailability from AUCinf,pred bioavailability uncommon
f.int.obs Bioavailability from AUCint,inf,obs bioavailability uncommon
f.int.pred Bioavailability from AUCint,inf,pred bioavailability uncommon
mrt.obs MRT to inf, observed Clast mrt uncommon
mrt.pred MRT to inf, predicted Clast mrt uncommon
mrt.int.inf.obs MRT, interval AUCinf,obs/AUMCinf,obs mrt uncommon
mrt.int.inf.pred MRT, interval AUCinf,pred/AUMCinf,pred mrt uncommon
mrt.iv.obs IV MRT, AUCinf,obs/AUMCinf,obs mrt uncommon
mrt.iv.pred IV MRT, AUCinf,pred/AUMCinf,pred mrt uncommon
mrt.md.obs MRT, multi-dose AUCinf,obs/AUMCinf,obs mrt uncommon
mrt.md.pred MRT, multi-dose AUCinf,pred/AUMCinf,pred mrt uncommon
mrt.ivmd.obs IV MRT, multi-dose, AUCinf,obs mrt uncommon
mrt.ivmd.pred IV MRT, multi-dose, AUCinf,pred mrt uncommon
vz.obs Vz, observed Clast volume_z uncommon
vz.pred Vz, predicted Clast volume_z uncommon
vz.int.inf.obs Vz, interval AUCint,inf,obs volume_z uncommon
vz.int.inf.pred Vz, interval AUCint,inf,pred volume_z uncommon
vz.iv.obs IV Vz, observed AUCinf volume_z uncommon
vz.iv.pred IV Vz, predicted AUCinf volume_z uncommon
vz.sparse.last Vz from sparse sampling volume_z uncommon
vss.obs Vss, observed Clast volume_ss uncommon
vss.pred Vss, predicted Clast volume_ss uncommon
vss.iv.obs IV Vss, observed Clast volume_ss uncommon
vss.iv.pred IV Vss, predicted Clast volume_ss uncommon
vss.md.obs Vss, multi-dose, obs volume_ss uncommon
vss.md.pred Vss, multi-dose, pred volume_ss uncommon
vss.ivmd.obs IV Vss, multi-dose, obs volume_ss uncommon
vss.ivmd.pred IV Vss, multi-dose, pred volume_ss uncommon
vss.int.inf.obs Vss, calc from interval AUCint,inf,obs volume_ss uncommon
vss.int.inf.pred Vss, calc from interval AUCint,inf,pred volume_ss uncommon
cav.int.inf.obs Avg conc in interval (AUCint,inf,obs) average_conc uncommon
cav.int.inf.pred Avg conc in interval (AUCint,inf,pred) average_conc uncommon
ratio.aucinf.obs Ratio of AUCinf,obs to reference parameter_ratio uncommon
ratio.aucinf.pred Ratio of AUCinf,pred to reference parameter_ratio uncommon
thalf.eff.obs Effective half-life, MRTobs effective_half_life uncommon
thalf.eff.pred Effective half-life, MRTpred effective_half_life uncommon
thalf.eff.iv.obs Effective half-life, IV MRTobs effective_half_life uncommon
thalf.eff.iv.pred Effective half-life, IV MRTpred effective_half_life uncommon
kel.obs Elim rate, MRT w/ obs Clast elimination_rate uncommon
kel.pred Elim rate, MRT w/ pred Clast elimination_rate uncommon
kel.iv.obs Elim rate, IV MRTobs elimination_rate uncommon
kel.iv.pred Elim rate, IV MRTpred elimination_rate uncommon
kel.int.inf.obs Elim rate, MRTint,inf,obs elimination_rate uncommon
kel.int.inf.pred Elim rate, MRTint,inf,pred elimination_rate uncommon
auclast.dn Dose-normalized auclast auc uncommon
aucall.dn Dose-normalized aucall auc uncommon
aucinf.obs.dn Dose-normalized aucinf.obs auc uncommon
aucinf.pred.dn Dose-normalized aucinf.pred auc uncommon
aumclast.dn Dose-normalized aumclast aumc uncommon
aumcall.dn Dose-normalized aumcall aumc uncommon
aumcinf.obs.dn Dose-normalized aumcinf.obs aumc uncommon
aumcinf.pred.dn Dose-normalized aumcinf.pred aumc uncommon
cmax.dn Dose-normalized cmax peak_conc uncommon
cmin.dn Dose-normalized cmin min_conc uncommon
clast.obs.dn Dose-normalized clast.obs last_conc uncommon
clast.pred.dn Dose-normalized clast.pred last_conc uncommon
cav.dn Dose-normalized cav average_conc uncommon
ctrough.dn Dose-normalized ctrough trough_conc uncommon
clr.last.dn Dose-normalized clr.last renal_clearance uncommon
clr.obs.dn Dose-normalized clr.obs renal_clearance uncommon
clr.pred.dn Dose-normalized clr.pred renal_clearance uncommon

Parameters fall into broad families:

  • AUC / AUMC — auclast, aucall, aucinf.obs, aucinf.pred, aumclast, aumcall, aumcinf.*, the interval-restricted aucint.* and aumcint.* variants, and aucabove.*
  • Concentration — cmax, cmin, ctrough, clast.obs, clast.pred, c0, cstart, ceoi, cav
  • Time — tmax, tmin, tlast, tfirst, tlag, time_above
  • Clearance / volume / MRT — the cl.*, vz.*, vss.*, mrt.*, and kel.* families, each in observed, predicted, last, all, interval, and IV forms (for example cl.obs, cl.last, cl.int.inf.obs, cl.iv.obs)
  • Elimination — half.life, lambda.z, lambda.z.n.points, span.ratio, r.squared, adj.r.squared, lambda.z.corrxy, and the Tobit outputs
  • Excretion — ae, fe, volpk, clr.*, ermax, ertmax, ertlst, erint, erlst
  • Steady-state / multiple-dose — deg.fluc, swing, ptr, mrt.md.*, vss.md.*, mrt.ivmd.*, vss.ivmd.*, thalf.eff.*
  • Secondary (reference interval needed) — bioavailability f.obs, f.pred, f.last, f.int.* and the ratio.* family (see the Secondary Parameters chapter)
  • Sparse estimator outputs — auclast_se, auclast_df, and the other _se and _df companions, which arrive with the sparse estimates; the catalog also still lists the deprecated sparse_* and *.sparse.last names (see the Sparse Sampling chapter)
  • IV-specific — aucivlast, aucivall, aucivinf.*, aumciv*, aucivpbext*
  • Dose-normalized — auclast.dn, cmax.dn, and other *.dn variants
  • Counts and dose — count_conc, count_conc_measured, totdose

If a parameter you need is not in the catalog, you can add it — see the Writing Custom Parameter Functions chapter and the vignette Writing PKNCA Parameter Functions.


3.11 Checking parameter dependencies with get.parameter.deps()

Some parameters depend on others being computed first. By default, get.parameter.deps() returns all parameters that use a given parameter as an input, which answers “what becomes invalid if this parameter changes?”:

# Which parameters depend on auclast?
get.parameter.deps("auclast")
 [1] "aucivall_df"       "aucivall_se"       "aucivinf.obs_df"  
 [4] "aucivinf.obs_se"   "aucivlast"         "aucivlast_df"     
 [7] "aucivlast_se"      "aucivpbextlast"    "auclast"          
[10] "auclast_df"        "auclast_se"        "auclast.dn"       
[13] "aucpext.obs"       "aucpext.pred"      "cav"              
[16] "cav.dn"            "cl.iv.last"        "cl.last"          
[19] "deg.fluc"          "f.last"            "kel.iv.last"      
[22] "kel.last"          "mrt.iv.last"       "mrt.ivmd.obs"     
[25] "mrt.ivmd.pred"     "mrt.last"          "mrt.md.obs"       
[28] "mrt.md.pred"       "ratio.auclast"     "thalf.eff.iv.last"
[31] "thalf.eff.last"    "vss.iv.last"       "vss.ivmd.obs"     
[34] "vss.ivmd.pred"     "vss.last"          "vss.md.obs"       
[37] "vss.md.pred"       "vz.iv.last"        "vz.last"          

This is useful when you plan to modify or exclude a parameter and want to know which downstream parameters depend on it. Dependencies are resolved automatically — you do not need to set dependency flags to TRUE manually.

recursive = TRUE (≥ 0.13.0) searches in the other direction, answering “what does this parameter need?”. It follows each dependency back through the intermediate parameters and returns only what they end at: data inputs such as conc, time, and dose, and the other arguments of the calculation functions, such as options (intermediate parameters such as aucinf.obs are not listed):

get.parameter.deps("auclast", recursive = TRUE)
[1] "conc"    "options" "time"   
get.parameter.deps("cl.obs", recursive = TRUE)
 [1] "adj.r.squared.factor"     "allow.tmax.in.half.life" 
 [3] "check"                    "conc"                    
 [5] "conc.blq"                 "conc.na"                 
 [7] "dose"                     "duration.dose"           
 [9] "first.tmax"               "hl_method"               
[11] "lloq"                     "manually.selected.points"
[13] "max.hl.points"            "min.hl.points"           
[15] "min.hl.start.time"        "options"                 
[17] "r.squared.factor"         "time"                    
[19] "time.dose"                "tobit_n_points_penalty"  
[21] "tobit_optim_control"     

3.12 Validating intervals

Interval specifications are checked at two levels.

check.interval.specification() checks the specification on its own: that start and end are present and valid (start < end, non-missing start), that known parameter columns hold valid values, and it warns when a row requests no parameters. Unknown extra columns pass through untouched (deliberately, so grouping columns survive).

# A valid interval
valid_int <- data.frame(
  start      = c(0, 0),
  end        = c(24, Inf),
  auclast    = c(TRUE, FALSE),
  aucinf.obs = c(FALSE, TRUE),
  cmax       = c(FALSE, TRUE)
)
valid_checked <- check.interval.specification(valid_int)

# Every registered parameter column is filled in ...
ncol(valid_checked)
[1] 237
# ... but only the requested ones are TRUE
valid_checked |>
  select(where(~ !is.logical(.x) || any(.x)))
  start end auclast  cmax aucinf.obs
1     0  24    TRUE FALSE      FALSE
2     0 Inf   FALSE  TRUE       TRUE

An invalid interval (start >= end) is an error; here it is caught to show its class and message:

bad_int <- data.frame(
  start   = 24,
  end     = 0,
  auclast = TRUE
)
tryCatch(
  check.interval.specification(bad_int),
  error = function(e) c(class = class(e)[1], message = conditionMessage(e))
)
                               class                              message 
"pknca_error_interval_start_gte_end"                "start must be < end" 

assert_intervals() checks a specification against a PKNCAdata object, and set_intervals() checks it and then stores it in the object. They run whenever intervals are given to PKNCAdata(), and pk.nca() runs assert_intervals() again, so intervals assigned directly to o_data$intervals are checked too (≥ 0.13.0). Beyond the checks above, they require that every column is a group column, a parameter, the imputation column, conc_above, tau, interval_id, a _ref column, interval_time_kind, or listed in keep_interval_cols; that every start is finite and not missing; and that every end is after its start (Inf is allowed, but not missing, NaN, or -Inf). The errors name the offending columns or rows:

o_data_check <- PKNCAdata(o_conc, o_dose, intervals = valid_int)

# Replace the intervals after checking them
o_data_check <- set_intervals(o_data_check, data.frame(start = 0, end = Inf, cmax = TRUE))
o_data_check$intervals
  start end cmax
1     0 Inf TRUE
show_error <- function(expr) {
  tryCatch(expr, error = function(e) c(class = class(e)[1], message = conditionMessage(e)))
}
# A column that is neither a group, a parameter, nor kept
show_error(set_intervals(o_data_check, data.frame(start = 0, end = 24, auclast = TRUE, visit = 1)))
                                                        class 
                       "pknca_error_invalid_interval_columns" 
                                                      message 
"The following columns in 'intervals' are not allowed: visit" 
# An end that is missing
show_error(set_intervals(o_data_check, data.frame(start = 0, end = NA_real_, auclast = TRUE)))
                                                                       class 
                                          "pknca_error_interval_end_invalid" 
                                                                     message 
"Interval `end` must not be missing, NaN, or -Inf (Inf is allowed); rows: 1" 
# An end before its start
show_error(set_intervals(o_data_check, bad_int))
                                          class 
     "pknca_error_interval_end_not_after_start" 
                                        message 
"Interval `end` must be after `start`; rows: 1" 

To carry an extra column such as visit into the results, list it in the keep_interval_cols option instead (see the Workflow chapter).

check.interval.specification() returns the checked, column-completed specification — every registered parameter column filled in with its default — so it can be used inline before passing to PKNCAdata():

o_conc <- PKNCAconc(Theoph, conc ~ Time | Subject)
o_dose <- PKNCAdose(
  Theoph |> group_by(Subject) |>
    summarise(Dose = Dose[1] * Wt[1], .groups = "drop") |>
    mutate(Time = 0),
  Dose ~ Time | Subject
)

my_intervals <- check.interval.specification(
  data.frame(
    start      = c(0, 0),
    end        = c(24, Inf),
    auclast    = c(TRUE, FALSE),
    aucinf.obs = c(FALSE, TRUE),
    cmax       = c(FALSE, TRUE),
    half.life  = c(FALSE, TRUE)
  )
)

o_data <- PKNCAdata(o_conc, o_dose, intervals = my_intervals)
o_nca <- pk.nca(o_data)
as.data.frame(o_nca) |> head(8)
# A tibble: 8 × 7
  Subject start   end PPTESTCD      PPORRES PPANMETH               exclude
  <ord>   <dbl> <dbl> <chr>           <dbl> <chr>                  <chr>  
1 6           0    24 auclast       71.7    "AUC: lin up/log down" <NA>   
2 6           0   Inf cmax           6.44   ""                     <NA>   
3 6           0   Inf tmax           1.15   ""                     <NA>   
4 6           0   Inf tlast         23.8    ""                     <NA>   
5 6           0   Inf clast.obs      0.92   ""                     <NA>   
6 6           0   Inf lambda.z       0.0878 ""                     <NA>   
7 6           0   Inf r.squared      0.998  ""                     <NA>   
8 6           0   Inf adj.r.squared  0.998  ""                     <NA>