AUC Calculation Choices: Linear vs Log Trapezoidal and Why It Matters

Understand how AUC is computed in NCA, when linear vs log trapezoidal rules are appropriate, and how calculation choices can change results.
Tip

Big idea: AUC is “just integration,” but the integration rule is a choice. Linear vs log trapezoidal assumptions can change AUC — especially on the declining phase.

Learning Objectives

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

  • Explain what the trapezoidal rule is doing conceptually.
  • Describe when linear trapezoidal vs log trapezoidal is more appropriate.
  • Run NCA in PKNCA and extract AUC outputs robustly across environments.
  • Communicate how and why AUC rules affect interpretation.

Key Ideas

  • NCA estimates AUC by integrating concentration over time using trapezoids.
  • Linear trapezoidal connects points with straight lines.
  • Log trapezoidal is often used on the declining phase because PK decline is frequently exponential.
  • Many workflows use linear-up / log-down (linear on the rising phase, log on the declining phase).
  • PKNCA uses linear-up/log-down by default.
  • Differences are usually small in rich data — but can matter in sparse sampling or steep declines.

Conceptual Warm-Up: What the Trapezoidal Rule Is Doing

For two adjacent timepoints \((t_1, C_1)\) and \((t_2, C_2)\):

Linear trapezoid

\[ AUC_{1\to2}^{(lin)} = \frac{(C_1 + C_2)}{2} (t_2 - t_1) \]

Log trapezoid (declining phase)

When \(C_2 < C_1\) and decline is approximately exponential, a common log trapezoid form is:

\[ AUC_{1\to2}^{(log)} = \frac{(C_1 - C_2)}{\ln(C_1) - \ln(C_2)} (t_2 - t_1) \]

Warning

Log trapezoidal requires positive concentrations. If you have zeros/BLQ values, you must handle them intentionally.


Setup: Run NCA on Theoph (Baseline)

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,
  auclast = TRUE,
  aucinf.obs = TRUE
)

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

Run NCA Using Linear-Up / Log-Down (Default)

PKNCA.options(auc.method = "lin up/log down")

res_default <- pk.nca(nca_data)
raw_default <- as.data.frame(res_default)

sort(unique(raw_default$PPTESTCD))
 [1] "adj.r.squared"       "aucinf.obs"          "auclast"            
 [4] "clast.obs"           "clast.pred"          "half.life"          
 [7] "lambda.z"            "lambda.z.n.points"   "lambda.z.time.first"
[10] "lambda.z.time.last"  "r.squared"           "span.ratio"         
[13] "tlast"               "tmax"               

Run NCA Using Pure Linear Trapezoidal

PKNCA.options(auc.method = "linear")

res_linear <- pk.nca(nca_data)
raw_linear <- as.data.frame(res_linear)

Extract AUC Results

Across versions/settings, the AUC to the last observed concentration is reported as auclast. We can extract it together with aucinf.obs after reshaping the results table.

extract_auc <- function(raw_df) {
  raw_df %>%
    filter(PPTESTCD %in% c("aucinf.obs", "auclast")) %>%
    select(ID, PPTESTCD, PPORRES) %>%
    pivot_wider(names_from = PPTESTCD, values_from = PPORRES) %>%
    select(ID, auclast, aucinf.obs)
}

auc_default <- extract_auc(raw_default)
auc_linear  <- extract_auc(raw_linear)

Compare Results

compare_auc <- auc_default %>%
  rename(
    auclast_default = auclast,
    aucinf_default  = aucinf.obs
  ) %>%
  inner_join(
    auc_linear %>%
      rename(
        auclast_linear = auclast,
        aucinf_linear  = aucinf.obs
      ),
    by = "ID"
  ) %>%
  mutate(
    d_auclast = auclast_linear - auclast_default,
    d_aucinf  = aucinf_linear  - aucinf_default
  )

compare_auc %>% head()
# A tibble: 6 × 7
  ID    auclast_default aucinf_default auclast_linear aucinf_linear d_auclast
  <ord>           <dbl>          <dbl>          <dbl>         <dbl>     <dbl>
1 6                71.7           82.2           73.8          84.3      2.08
2 7                88.0          101.            90.8         104.       2.78
3 8                86.8          102.            88.6         104.       1.75
4 11               77.9           86.9           80.1          89.1      2.20
5 3                95.9          106.            99.3         110.       3.41
6 2                88.7           97.4           91.5         100.       2.80
# ℹ 1 more variable: d_aucinf <dbl>

This comparison shows how the integration rule alone can slightly change the estimated exposure.


How to Communicate the Impact

When AUC differs between rules, explain why:

  • Linear assumes straight-line change between samples.
  • Log assumes exponential decline (often realistic post-peak).
  • Differences tend to show up most on the declining phase and with sparse sampling.

In regulated contexts, consistency matters most:

Use the same rule across arms/studies when comparing exposure.


Strategies

  • Default to reproducibility: document the rule and software version.
  • Use linear-up/log-down when justified.
  • Treat rule choice as an assumption: “this is the curve shape between samples.”
  • When comparing arms, keep the rule fixed.

Common Mistakes

Warning
  • Assuming AUC is “objective” without specifying interpolation rules.
  • Treating software defaults as invisible rather than documented assumptions.
  • Using log methods with non-positive concentrations.
  • Forgetting sparse sampling makes rule choice more impactful.

Practice Problems

  1. Conceptual: On which part of a typical oral PK curve would you expect log trapezoidal to matter most, and why?
  2. Executable: Build a subject-level table with auclast and aucinf.obs.
  3. Executable: Compute the percent extrapolated contribution: \(100\times(AUC_{0-\infty}-AUC_{0-tlast})/AUC_{0-\infty}\).

1. Conceptual:
On the declining phase. If decline is approximately exponential, log trapezoids match the curve shape between samples better than straight lines.

2. Subject-level AUC table:

auc_tbl <- extract_auc(raw_default)
auc_tbl %>% head()
# A tibble: 6 × 3
  ID    auclast aucinf.obs
  <ord>   <dbl>      <dbl>
1 6        71.7       82.2
2 7        88.0      101. 
3 8        86.8      102. 
4 11       77.9       86.9
5 3        95.9      106. 
6 2        88.7       97.4

3. Percent extrapolated contribution:

auc_tbl %>%
  mutate(
    pct_extra = 100 * (aucinf.obs - auclast) / aucinf.obs
  ) %>%
  arrange(desc(pct_extra)) %>%
  head(12)
# A tibble: 12 × 4
   ID    auclast aucinf.obs pct_extra
   <ord>   <dbl>      <dbl>     <dbl>
 1 1       147.       215.      31.5 
 2 10      136.       168.      19.2 
 3 8        86.8      102.      15.0 
 4 9        83.9       97.5     13.9 
 5 5       118.       136.      13.3 
 6 7        88.0      101.      12.9 
 7 6        71.7       82.2     12.8 
 8 11       77.9       86.9     10.4 
 9 4       103.       114.      10.1 
10 3        95.9      106.       9.66
11 2        88.7       97.4      8.88
12 12      115.       126.       8.43

Summary

AUC is estimated, not directly observed.

  • Linear trapezoids assume straight-line change.
  • Log trapezoids assume exponential decline (often more realistic post-peak).
  • PKNCA allows global or calculation-specific control of the interpolation rule.
  • The interpolation rule is an assumption that should be documented.
  • Consistency matters most when comparing exposure across arms/studies.

  • Document your software version and interpolation rule.
  • Expect differences mainly on the declining phase (and in sparse sampling).
  • Don’t use log trapezoids with zeros/BLQ without a plan.
  • When comparing arms, keep rules fixed and document assumptions.