Running pk.nca() and Exploring the PKNCAresults Object

Execute the full NCA calculation with pk.nca(), inspect the PKNCAresults object, and understand how raw results differ from summarized outputs.
Tip

Big idea: Running pk.nca() produces structured results — not a final report. Understanding the PKNCAresults object is essential before summarizing or exporting.

Learning Objectives

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

  • Run NCA calculations using pk.nca().
  • Inspect the structure of a PKNCAresults object.
  • Distinguish between raw NCA results and summarized outputs.
  • Extract results for further inspection or reporting.
  • Interpret clearance correctly when dose is recorded in mg/kg.

Key Ideas

  • pk.nca() performs the actual exposure calculations.
  • The result is stored in a PKNCAresults object.
  • Raw results contain subject-level parameter estimates.
  • summary() produces report-ready summaries.
  • Calculation and reporting are deliberately separated.
  • Dose units propagate into clearance units.

Recreate Structured NCA Data

library(tidyverse)
library(PKNCA)

data(Theoph)

theoph_conc <- as_tibble(Theoph) %>%
  transmute(ID = Subject, TIME = Time, CONC = conc)

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

conc_obj <- PKNCAconc(CONC ~ TIME | ID, data = theoph_conc)
dose_obj <- PKNCAdose(DOSE ~ TIME | ID, data = theoph_dose)

intervals_df <- data.frame(
  start = 0,
  end = Inf,
  cmax = TRUE,
  auclast = TRUE,
  aucinf.obs = TRUE,
  half.life = TRUE,
  cl.obs = TRUE
)

nca_data <- PKNCAdata(conc_obj, dose_obj, intervals = intervals_df)

Running pk.nca()

results_obj <- pk.nca(nca_data)
results_obj
$result
# A tibble: 192 × 6
   ID    start   end PPTESTCD            PPORRES exclude
   <ord> <dbl> <dbl> <chr>                 <dbl> <chr>  
 1 6         0   Inf auclast             71.7    <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>   
 9 6         0   Inf lambda.z.time.first  2.03   <NA>   
10 6         0   Inf lambda.z.time.last  23.8    <NA>   
# ℹ 182 more rows

$data
Formula for concentration:
 CONC ~ TIME | ID
Data are dense PK.
With 12 subjects defined in the 'ID' column.
Nominal time column is not specified.

First 6 rows of concentration data:
 ID TIME  CONC exclude volume duration
  1 0.00  0.74    <NA>     NA        0
  1 0.25  2.84    <NA>     NA        0
  1 0.57  6.57    <NA>     NA        0
  1 1.12 10.50    <NA>     NA        0
  1 2.02  9.66    <NA>     NA        0
  1 3.82  8.58    <NA>     NA        0
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

With 1 rows of interval specifications.
With imputation: NA
Options changed from default are:
$adj.r.squared.factor
[1] 1e-04

$max.missing
[1] 0.5

$auc.method
[1] "lin up/log down"

$conc.na
[1] "drop"

$conc.blq
$conc.blq$first
[1] "keep"

$conc.blq$middle
[1] "drop"

$conc.blq$last
[1] "keep"


$debug
NULL

$first.tmax
[1] TRUE

$allow.tmax.in.half.life
[1] FALSE

$keep_interval_cols
NULL

$min.hl.points
[1] 3

$min.span.ratio
[1] 2

$max.aucinf.pext
[1] 20

$min.hl.r.squared
[1] 0.9

$progress
[1] TRUE

$tau.choices
[1] NA

$single.dose.aucs
  start end auclast aucall aumclast aumcall aucint.last aucint.last.dose
1     0  24    TRUE  FALSE    FALSE   FALSE       FALSE            FALSE
2     0 Inf   FALSE  FALSE    FALSE   FALSE       FALSE            FALSE
  aucint.all aucint.all.dose    c0  cmax  cmin  tmax tlast tfirst clast.obs
1      FALSE           FALSE FALSE FALSE FALSE FALSE FALSE  FALSE     FALSE
2      FALSE           FALSE FALSE  TRUE FALSE  TRUE FALSE  FALSE     FALSE
  cl.last cl.all     f mrt.last mrt.iv.last vss.last vss.iv.last   cav
1   FALSE  FALSE FALSE    FALSE       FALSE    FALSE       FALSE FALSE
2   FALSE  FALSE FALSE    FALSE       FALSE    FALSE       FALSE FALSE
  cav.int.last cav.int.all ctrough cstart   ptr  tlag deg.fluc swing  ceoi
1        FALSE       FALSE   FALSE  FALSE FALSE FALSE    FALSE FALSE FALSE
2        FALSE       FALSE   FALSE  FALSE FALSE FALSE    FALSE FALSE FALSE
  aucabove.predose.all aucabove.trough.all count_conc count_conc_measured
1                FALSE               FALSE      FALSE               FALSE
2                FALSE               FALSE      FALSE               FALSE
  totdose    ae clr.last clr.obs clr.pred    fe sparse_auclast sparse_auc_se
1   FALSE FALSE    FALSE   FALSE    FALSE FALSE          FALSE         FALSE
2   FALSE FALSE    FALSE   FALSE    FALSE FALSE          FALSE         FALSE
  sparse_auc_df time_above aucivlast aucivall aucivint.last aucivint.all
1         FALSE      FALSE     FALSE    FALSE         FALSE        FALSE
2         FALSE      FALSE     FALSE    FALSE         FALSE        FALSE
  aucivpbextlast aucivpbextall aucivpbextint.last aucivpbextint.all half.life
1          FALSE         FALSE              FALSE             FALSE     FALSE
2          FALSE         FALSE              FALSE             FALSE      TRUE
  r.squared adj.r.squared lambda.z lambda.z.time.first lambda.z.time.last
1     FALSE         FALSE    FALSE               FALSE              FALSE
2     FALSE         FALSE    FALSE               FALSE              FALSE
  lambda.z.n.points clast.pred span.ratio thalf.eff.last thalf.eff.iv.last
1             FALSE      FALSE      FALSE          FALSE             FALSE
2             FALSE      FALSE      FALSE          FALSE             FALSE
  kel.last kel.iv.last aucinf.obs aucinf.pred aumcinf.obs aumcinf.pred
1    FALSE       FALSE      FALSE       FALSE       FALSE        FALSE
2    FALSE       FALSE       TRUE       FALSE       FALSE        FALSE
  aucint.inf.obs aucint.inf.obs.dose aucint.inf.pred aucint.inf.pred.dose
1          FALSE               FALSE           FALSE                FALSE
2          FALSE               FALSE           FALSE                FALSE
  aucivinf.obs aucivinf.pred aucivpbextinf.obs aucivpbextinf.pred aucpext.obs
1        FALSE         FALSE             FALSE              FALSE       FALSE
2        FALSE         FALSE             FALSE              FALSE       FALSE
  aucpext.pred cl.obs cl.pred mrt.obs mrt.pred mrt.iv.obs mrt.iv.pred
1        FALSE  FALSE   FALSE   FALSE    FALSE      FALSE       FALSE
2        FALSE  FALSE   FALSE   FALSE    FALSE      FALSE       FALSE
  mrt.md.obs mrt.md.pred vz.obs vz.pred vss.obs vss.pred vss.iv.obs vss.iv.pred
1      FALSE       FALSE  FALSE   FALSE   FALSE    FALSE      FALSE       FALSE
2      FALSE       FALSE  FALSE   FALSE   FALSE    FALSE      FALSE       FALSE
  vss.md.obs vss.md.pred cav.int.inf.obs cav.int.inf.pred thalf.eff.obs
1      FALSE       FALSE           FALSE            FALSE         FALSE
2      FALSE       FALSE           FALSE            FALSE         FALSE
  thalf.eff.pred thalf.eff.iv.obs thalf.eff.iv.pred kel.obs kel.pred kel.iv.obs
1          FALSE            FALSE             FALSE   FALSE    FALSE      FALSE
2          FALSE            FALSE             FALSE   FALSE    FALSE      FALSE
  kel.iv.pred auclast.dn aucall.dn aucinf.obs.dn aucinf.pred.dn aumclast.dn
1       FALSE      FALSE     FALSE         FALSE          FALSE       FALSE
2       FALSE      FALSE     FALSE         FALSE          FALSE       FALSE
  aumcall.dn aumcinf.obs.dn aumcinf.pred.dn cmax.dn cmin.dn clast.obs.dn
1      FALSE          FALSE           FALSE   FALSE   FALSE        FALSE
2      FALSE          FALSE           FALSE   FALSE   FALSE        FALSE
  clast.pred.dn cav.dn ctrough.dn
1         FALSE  FALSE      FALSE
2         FALSE  FALSE      FALSE

$allow_partial_missing_units
[1] FALSE


$columns
$columns$exclude
[1] "exclude"


attr(,"class")
[1] "PKNCAresults" "list"        
attr(,"provenance")
Provenance hash 3058fbcf424973705eed8bbb1b2053d3 generated on 2026-05-29 22:18:06.490661 with R version 4.5.2 (2025-10-31).

This produces a PKNCAresults object containing subject-level parameter estimates.


Inspecting the Results Object

Check its class:

class(results_obj)
[1] "PKNCAresults" "list"        

Convert to a data frame for inspection:

raw_df <- as.data.frame(results_obj)
raw_df %>% head(12)
# A tibble: 12 × 6
   ID    start   end PPTESTCD            PPORRES exclude
   <ord> <dbl> <dbl> <chr>                 <dbl> <chr>  
 1 6         0   Inf auclast             71.7    <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>   
 9 6         0   Inf lambda.z.time.first  2.03   <NA>   
10 6         0   Inf lambda.z.time.last  23.8    <NA>   
11 6         0   Inf lambda.z.n.points    7      <NA>   
12 6         0   Inf clast.pred           0.941  <NA>   

Each row represents:

  • One subject
  • One interval
  • One parameter

Common parameters include:

  • cmax
  • tmax
  • auclast
  • aucinf.obs
  • half.life
  • cl.obs

Important: Interpreting Clearance Units

Note

In Theoph, dose is recorded in mg/kg.

Since:

\[ CL/F = \frac{\text{Dose}}{AUC_{\infty}} \]

and dose is in mg/kg while AUC is in mg·h/L,

clearance will be reported in:

\[ \mathrm{L/h/kg} \]

That means cl.obs here is weight-normalized clearance, not total clearance (L/h).

Always verify dose units before interpreting clearance values.


Understanding Raw vs Summary Results

Raw results:

raw_df %>% head()
# A tibble: 6 × 6
  ID    start   end PPTESTCD  PPORRES exclude
  <ord> <dbl> <dbl> <chr>       <dbl> <chr>  
1 6         0   Inf auclast   71.7    <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>   

Summarized results:

summary_obj <- summary(results_obj)
summary_obj
 start end  N     auclast        cmax   half.life aucinf.obs        cl.obs
     0 Inf 12 98.7 [22.5] 8.65 [17.0] 8.18 [2.12] 115 [28.4] 0.0398 [29.4]

Caption: auclast, cmax, aucinf.obs, cl.obs: geometric mean and geometric coefficient of variation; half.life: arithmetic mean and standard deviation; N: number of subjects

summary() aggregates across subjects and produces statistics such as:

  • Mean
  • SD
  • CV%
  • Geometric mean (for selected parameters)

Extracting Specific Parameters

Filter for AUC and half-life:

raw_df %>%
  filter(PPTESTCD %in% c("auclast", "aucinf.obs", "half.life")) %>%
  select(ID, PPTESTCD, PPORRES) %>%
  head(12)
# A tibble: 12 × 3
   ID    PPTESTCD   PPORRES
   <ord> <chr>        <dbl>
 1 6     auclast      71.7 
 2 6     half.life     7.89
 3 6     aucinf.obs   82.2 
 4 7     auclast      88.0 
 5 7     half.life     7.85
 6 7     aucinf.obs  101.  
 7 8     auclast      86.8 
 8 8     half.life     8.51
 9 8     aucinf.obs  102.  
10 11    auclast      77.9 
11 11    half.life     7.26
12 11    aucinf.obs   86.9 

You can also extract clearance explicitly:

raw_df %>%
  filter(PPTESTCD == "cl.obs") %>%
  select(ID, PPORRES) %>%
  head()
# A tibble: 6 × 2
  ID    PPORRES
  <ord>   <dbl>
1 6      0.0487
2 7      0.0490
3 8      0.0443
4 11     0.0566
5 3      0.0427
6 2      0.0452

Strategies

  • Always inspect raw results before summarizing.
  • Confirm expected parameters are present.
  • Check half-life values for plausibility.
  • Verify units before interpreting clearance.
  • Treat summary output as reporting layer, not validation layer.

Common Mistakes

Warning
  • Treating summary() as validation.
  • Reporting AUCinf when extrapolated fraction is large.
  • Ignoring terminal slope diagnostics.
  • Comparing L/h/kg to L/h without recognizing scaling differences.

Practice Problems

Executable

  1. Count how many unique parameters were computed.
  2. Extract only Cmax values and compute their mean manually using dplyr.
  3. Identify the subject with the largest half-life.

Conceptual

  1. Why is it important to inspect raw results before calling summary()?
  2. Why must you verify dose units before interpreting clearance?

1. Count parameters:

raw_df %>%
  summarise(n_parameters = n_distinct(PPTESTCD))
# A tibble: 1 × 1
  n_parameters
         <int>
1           16

2. Manual Cmax mean:

raw_df %>%
  filter(PPTESTCD == "cmax") %>%
  summarise(mean_cmax = mean(PPORRES, na.rm = TRUE))
# A tibble: 1 × 1
  mean_cmax
      <dbl>
1      8.76

3. Largest half-life:

raw_df %>%
  filter(PPTESTCD == "half.life") %>%
  arrange(desc(PPORRES)) %>%
  slice(1)
# A tibble: 1 × 6
  ID    start   end PPTESTCD  PPORRES exclude
  <ord> <dbl> <dbl> <chr>       <dbl> <chr>  
1 1         0   Inf half.life    14.3 <NA>   

4. Conceptual reason:
Raw inspection ensures parameters are plausible and correctly calculated before statistical aggregation masks potential issues.

5. Clearance units:
If dose is in mg/kg, clearance is L/h/kg. Misinterpreting units can lead to incorrect clinical or modeling conclusions.


Summary

You have now:

  • Structured concentration data.
  • Structured dose data.
  • Defined intervals.
  • Executed pk.nca().
  • Inspected raw and summarized outputs.
  • Interpreted clearance correctly given dose units.

You now understand the full NCA calculation pipeline — and how to validate it before reporting.


  • Always inspect raw results first.
  • Confirm expected parameters are present.
  • Treat summary output as reporting, not validation.
  • Verify dose units before interpreting clearance.
  • Label units explicitly in reporting tables.