Building the Dose Object with PKNCAdose()

Construct and validate dosing records for NCA using PKNCAdose(), and align dose data correctly with concentration profiles.
Tip

Big idea: NCA exposure metrics depend on both concentration data and correctly structured dose data. If dose records are misaligned, parameters like CL/F will be wrong.

Learning Objectives

By the end of this lesson, you will be able to:

  • Identify the minimum required columns for dosing data in NCA.
  • Construct a PKNCAdose() object using a formula interface.
  • Align dose records with concentration profiles (matching grouping).
  • Perform QC checks to validate dose–concentration consistency.
  • Interpret the unit implications of dose recorded as mg/kg.

Key Ideas

  • Dose data must align with the same grouping structure as concentration data.
  • Dose timing defines the reference for time-zero.
  • Derived parameters like \(CL/F\) depend directly on accurate dose records.
  • PKNCAdose() defines dose structure — it does not compute NCA.
  • Units propagate: if dose is in mg/kg, clearance will be in \(\mathrm{L/h/kg}\).

Using Theoph Dose Information

The built-in dataset Theoph includes a column Dose recorded in mg/kg.
Each subject received a single oral dose at time 0.

We will build a clean dosing table with standardized column names.

library(tidyverse)
library(PKNCA)

data(Theoph)

theoph_dose <- as_tibble(Theoph) %>%
  distinct(Subject, Dose) %>%
  transmute(
    ID   = Subject,
    TIME = 0,
    DOSE = Dose
  )

theoph_dose %>%
  arrange(ID) %>%
  head(12)
# A tibble: 12 × 3
   ID     TIME  DOSE
   <ord> <dbl> <dbl>
 1 6         0  4   
 2 7         0  4.95
 3 8         0  4.53
 4 11        0  4.92
 5 3         0  4.53
 6 2         0  4.4 
 7 4         0  4.4 
 8 9         0  3.1 
 9 12        0  5.3 
10 10        0  5.5 
11 1         0  4.02
12 5         0  5.86
Note

Dose unit note (important): In Theoph, DOSE is in mg/kg, not mg.
That’s totally fine for teaching NCA, but it affects any parameter that uses dose (especially clearance).

For example, since \(CL/F = \text{Dose} / AUC_{\infty}\):

\[ \frac{\mathrm{mg/kg}}{\mathrm{mg\cdot h/L}} = \mathrm{L/h/kg} \]

So if you compute CL/F from this dataset, interpret it as weight-normalized clearance (L/h/kg).


What Columns Are Required?

A minimal dose dataset for NCA requires:

  • ID
  • TIME (dose time, in the same time scale as concentration data)
  • DOSE

Optional but commonly needed:

  • ROUTE
  • DURATION (for infusions)
  • PERIOD / OCC (multiple occasions)

Rule: dose grouping must match concentration grouping.


Quick Structural QC for Dose Data

Check that there is exactly one dose record per subject (for this single-dose example):

theoph_dose %>%
  count(ID, name = "n_dose_rows") %>%
  filter(n_dose_rows > 1)
# A tibble: 0 × 2
# ℹ 2 variables: ID <ord>, n_dose_rows <int>

That should return zero rows.

Check for missing or nonpositive doses:

theoph_dose %>%
  summarise(
    any_missing_dose = any(is.na(DOSE)),
    any_nonpositive_dose = any(DOSE <= 0)
  )
# A tibble: 1 × 2
  any_missing_dose any_nonpositive_dose
  <lgl>            <lgl>               
1 FALSE            FALSE               

Building the PKNCAdose Object

The PKNCAdose() interface is formula-based:

  • left side: dose amount
  • right side: dose time
  • grouping after |
dose_obj <- PKNCAdose(DOSE ~ TIME | ID, data = theoph_dose)
dose_obj
Formula for dosing:
 DOSE ~ TIME | ID
Nominal time column is not specified.

First 6 rows of dosing data:
 ID TIME DOSE exclude         route duration
  1    0 4.02    <NA> extravascular        0
  2    0 4.40    <NA> extravascular        0
  3    0 4.53    <NA> extravascular        0
  4    0 4.40    <NA> extravascular        0
  5    0 5.86    <NA> extravascular        0
  6    0 4.00    <NA> extravascular        0

Interpretation:

“DOSE is administered at TIME, and dose profiles are grouped by ID.”

No exposure metrics are calculated yet — this is a definition step.


Aligning Dose and Concentration Objects

Grouping must be consistent.

If concentration is defined as:

PKNCAconc(CONC ~ TIME | ID, data = conc_df)

Then dose must be defined as:

PKNCAdose(DOSE ~ TIME | ID, data = dose_df)

If you later include PERIOD or OCC for concentration, you must include it for dose too:

PKNCAconc(CONC ~ TIME | ID + PERIOD, data = conc_df)
PKNCAdose(DOSE ~ TIME | ID + PERIOD, data = dose_df)

Worked Example: Misaligned Grouping (Common Real-World Mistake)

Many studies include multiple occasions (crossover periods, repeat dosing days).
If you define concentration profiles by ID + PERIOD but define dose only by ID, you are mixing profiles.

Below is a synthetic two-period example to demonstrate the pattern.

conc_with_two_periods <- as_tibble(Theoph) %>%
  transmute(ID = Subject, TIME = Time, CONC = conc) %>%
  mutate(PERIOD = 1) %>%
  bind_rows(
    as_tibble(Theoph) %>%
      transmute(ID = Subject, TIME = Time, CONC = conc) %>%
      mutate(PERIOD = 2)
  )

dose_with_two_periods <- theoph_dose %>%
  mutate(PERIOD = 1) %>%
  bind_rows(theoph_dose %>% mutate(PERIOD = 2))

If we incorrectly define dose grouping as only ID, we lose the period structure:

bad_dose_attempt <- tryCatch(
  PKNCAdose(DOSE ~ TIME | ID, data = dose_with_two_periods),
  error = function(e) e
)

bad_dose_attempt
<simpleError in duplicate_check(object = ret, data_type = "dosing"): Rows that are not unique per group and time (column names: TIME, ID) found within dosing data.  Row numbers: 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24>

Correct grouping keeps periods distinct:

good_dose_attempt <- PKNCAdose(DOSE ~ TIME | ID + PERIOD, data = dose_with_two_periods)
good_dose_attempt
Formula for dosing:
 DOSE ~ TIME | ID + PERIOD
Nominal time column is not specified.

First 6 rows of dosing data:
 ID TIME DOSE PERIOD exclude         route duration
  1    0 4.02      1    <NA> extravascular        0
  2    0 4.40      1    <NA> extravascular        0
  3    0 4.53      1    <NA> extravascular        0
  4    0 4.40      1    <NA> extravascular        0
  5    0 5.86      1    <NA> extravascular        0
  6    0 4.00      1    <NA> extravascular        0

Strategies

  • Build the dose dataset separately from the concentration dataset.
  • Make “one profile” explicit (ID + occasion/analyte/matrix as needed).
  • Match grouping variables exactly between PKNCAconc() and PKNCAdose().
  • Validate structure before running NCA.
  • Label units explicitly in outputs (especially when dose is mg/kg).

Common Mistakes

Warning
  • Forgetting to include PERIOD (or OCC) in dose grouping.
  • Using an incorrect dose time (not zero when TIME is post-dose).
  • Mixing mg and mg/kg without documenting it.
  • Treating “code ran” as “dose structure is correct.”

Practice Problems

Conceptual

  1. Why must dose grouping match concentration grouping?
  2. If dose is recorded too high, what happens to \(CL/F\)? Why?

Executable

  1. Verify that theoph_dose has exactly one dose row per ID.
  2. Create duplicated dose rows and detect the issue using count().

1. Grouping alignment:
Exposure metrics are computed per profile. If dose grouping differs from concentration grouping, dose and concentration won’t align, and derived parameters become incorrect.

2. Incorrect dose and \(CL/F\):
Since \(CL/F = \text{Dose}/AUC_{\infty}\), an inflated dose inflates \(CL/F\) directly (linearly).

3. One dose per ID:

theoph_dose %>%
  count(ID, name = "n_dose_rows") %>%
  filter(n_dose_rows > 1)
# A tibble: 0 × 2
# ℹ 2 variables: ID <ord>, n_dose_rows <int>

4. Detect duplicated dose rows:

dup_dose <- bind_rows(theoph_dose, theoph_dose)

dup_dose %>%
  count(ID, name = "n_dose_rows") %>%
  filter(n_dose_rows > 1)
# A tibble: 12 × 2
   ID    n_dose_rows
   <ord>       <int>
 1 6               2
 2 7               2
 3 8               2
 4 11              2
 5 3               2
 6 2               2
 7 4               2
 8 9               2
 9 12              2
10 10              2
11 1               2
12 5               2

Summary

To construct dose data correctly for PKNCA:

  • Build a clean dataset with ID, TIME, and DOSE.
  • Match grouping to concentration data.
  • Validate structure before running NCA.
  • Remember: if DOSE is in mg/kg, any clearance you compute will be L/h/kg.

  • Dose time is typically 0 when TIME represents post-dose time.
  • Grouping must match between dose and concentration objects.
  • Validate one dose per profile unless study design differs.
  • Dose units propagate into clearance units — label them explicitly.