5  AUC Types and Integration Methods

5.1 AUC types

PKNCA offers four families of AUC — AUClast, AUCall, AUCinf (in observed and predicted flavors), and partial/interval AUC — each answering a different question. They differ in where integration ends and in how concentrations below the limit of quantification (BLQ) are treated:

Parameter End point BLQ treatment When to use
auclast Last measurable concentration BLQs after last non-BLQ are ignored Default; most studies
aucall Last measured timepoint Post-last-measurable BLQs treated as 0 When you want to include the BLQ tail
aucinf.obs Extrapolated to ∞ (using observed Clast) — When half-life is reliable
aucinf.pred Extrapolated to ∞ (using predicted Clast from λz fit) — When Clast is noisy
aucint.* Any user-defined time window (dose-aware, ≥ 0.13.0) Follows the .last, .all, or .inf suffix Partial AUC, bioequivalence

Two related families use the same suffixes: auciv* (IV bolus AUCs that back-extrapolate C0; see the IV dosing chapter) and, for sparse sampling, the _se/_df companions such as auclast_se (see the sparse NCA chapter).

Each AUC row in the pk.nca() results records its integration method in the PPANMETH column (≥ 0.13.0), for example "AUC: lin up/log down". A direct call such as pk.calc.auc.last() returns its value with the same text in a "method" attribute; use as.numeric() to drop it before comparing the value to a bare number.

For a worked example computing these AUC flavors on a full dataset, see the AUC Calculation with PKNCA vignette.

5.1.1 AUClast vs AUCall

Subject 1’s profile ends on a measurable sample (t = 24.37), so there is no BLQ tail and auclast equals aucall on the raw data. To show the difference, this demo appends a synthetic BLQ tail (concentrations reported as 0 at t = 26 and 28) to a copy of the data:

# Copy of subject 1's profile with a synthetic BLQ tail (reported as 0)
d_one_blq <- d_one |>
  select(subject, time, conc) |>
  mutate(subject = as.character(subject)) |>
  bind_rows(data.frame(subject = "1", time = c(26, 28), conc = 0))

o_conc_blq <- PKNCAconc(d_one_blq, conc ~ time | subject)

auc_compare <- data.frame(
  start    = 0,
  end      = Inf,
  auclast  = TRUE,
  aucall   = TRUE
)

o_nca_blq <- pk.nca(PKNCAdata(o_conc_blq, o_dose, intervals = auc_compare))

as.data.frame(o_nca_blq) |>
  filter(PPTESTCD %in% c("auclast", "aucall")) |>
  select(PPTESTCD, PPORRES)
# A tibble: 2 × 2
  PPTESTCD PPORRES
  <chr>      <dbl>
1 auclast     147.
2 aucall      150.

aucall ≥ auclast when there are post-last-measurable BLQ timepoints, since those are treated as 0 and add a small triangle to the AUC — here the triangle from Clast at t = 24.37 down to 0 at t = 26.


5.2 Partial AUC (AUCint)

For bioequivalence or exposure-in-a-window calculations, use aucint.* parameters. These calculate AUC over an exact time window, interpolating concentrations at the boundaries if needed.

# AUC from 0 to 4h and 0 to 12h on the same profile
partial_intervals <- data.frame(
  start         = c(0,  0),
  end           = c(4, 12),
  aucint.last   = TRUE   # AUC to the last obs within window (or interpolated boundary)
)

o_data_partial <- PKNCAdata(o_conc, o_dose, intervals = partial_intervals)
o_nca_partial  <- pk.nca(o_data_partial)

as.data.frame(o_nca_partial) |>
  filter(PPTESTCD == "aucint.last") |>
  select(start, end, PPTESTCD, PPORRES)
# A tibble: 2 × 4
  start   end PPTESTCD    PPORRES
  <dbl> <dbl> <chr>         <dbl>
1     0     4 aucint.last    33.7
2     0    12 aucint.last    91.7

All partial AUC variants:

Parameter Region after Tlast (within the window) Uses λz
aucint.last Contributes zero, as for auclast No
aucint.all Triangle from Clast down to the first BLQ sample, as for aucall No
aucint.inf.obs Extrapolated with λz from the observed Clast Only when the window extends past Tlast
aucint.inf.pred Extrapolated with λz from the predicted Clast Only when the window extends past Tlast

Every aucint.* parameter is dose-aware (≥ 0.13.0) whenever dosing data are given: it uses dose-aware interpolation (interp.extrap.conc.dose()) at the interval start and end, and it uses the route and duration of each dose, so an IV bolus is not treated as an extravascular dose. Three rules follow from that:

  • The profile ends at the next dose. The profile being integrated ends at the first dose at or after end, so a concentration measured after that dose is never interpolated back into the window. When the window covers a whole dosing interval, aucint.last and aucint.all equal auclast and aucall.
  • Only start and end are estimated. A dose inside the window is integrated across using the concentrations measured on either side of it; no concentration is imputed at that dose.
  • λz is used only when it is needed. A window that ends at or before Tlast is interpolated throughout, so aucint.inf.obs and aucint.inf.pred are reported, and are not excluded with the half-life, even when λz could not be estimated. When the window extends past Tlast and λz cannot be estimated, the .inf forms fall back to the aucint.all rule for a finite window.

When no dosing data are given, the calculation falls back to plain interpolation across the whole profile (interp.extrap.conc()). The PPANMETH column of the results (≥ 0.13.0) says which happened (“Interpolation: dose-aware” or “Interpolation: not dose-aware (no dosing data)”) and how the region after Tlast was handled (“Extrapolation: none”, “AUClast”, “AUCall”, or “half-life”).

Retired names: the .dose variants (aucint.last.dose, aucint.all.dose, aucint.inf.obs.dose, aucint.inf.pred.dose, and the matching aumcint.*.dose names) no longer exist because every aucint/aumcint parameter is now dose-aware. Requesting one is an error that names the parameter to use instead (for example, “‘aucint.last.dose’ is now named ‘aucint.last’”).

The full decision rules for each combination of doses and samples are in the dose-aware interpolation and extrapolation vignette.

# All partial AUC variants over a single window
all_partial <- data.frame(
  start            = 0,
  end              = 8,
  aucint.last      = TRUE,
  aucint.all       = TRUE,
  aucint.inf.obs   = TRUE,
  aucint.inf.pred  = TRUE
)

o_nca_all_partial <- pk.nca(PKNCAdata(o_conc, o_dose, intervals = all_partial))
as.data.frame(o_nca_all_partial) |>
  filter(grepl("^aucint", PPTESTCD)) |>
  select(PPTESTCD, PPORRES)
# A tibble: 4 × 2
  PPTESTCD        PPORRES
  <chr>             <dbl>
1 aucint.last        65.3
2 aucint.all         65.3
3 aucint.inf.obs     65.3
4 aucint.inf.pred    65.3

The 0–8 h window ends before subject 1’s Tlast (24.37 h), so the four variants are interpolated throughout and agree.

5.2.1 Why dose-awareness matters

The example below is a two-dose oral profile (doses at 0 and 12 h) whose 12-hour trough sample is missing, so the window 0–12 h has to end somewhere between the 8-hour sample and the post-dose 12.5-hour sample.

d_md <- data.frame(
  subject = "A",
  time = c(0, 0.5, 1, 2, 4, 6, 8, 12.5, 13, 14, 16, 20, 24),
  conc = c(0, 2.1, 3.6, 4.4, 3.5, 2.6, 2.0, 3.2, 4.6, 5.3, 4.2, 2.7, 1.7)
)
d_md_dose <- data.frame(subject = "A", time = c(0, 12), dose = 100)

o_conc_md <- PKNCAconc(d_md, conc ~ time | subject)
o_dose_md <- PKNCAdose(d_md_dose, dose ~ time | subject)

window_0_12 <- data.frame(
  start           = 0,
  end             = 12,
  aucint.last     = TRUE,
  aucint.inf.obs  = TRUE
)

# With dosing data: dose-aware
o_nca_aware <- pk.nca(PKNCAdata(o_conc_md, o_dose_md, intervals = window_0_12))
# Without dosing data: interpolation across the whole profile
o_nca_unaware <- pk.nca(PKNCAdata(o_conc_md, intervals = window_0_12))

d_compare <-
  bind_rows(
    as.data.frame(o_nca_aware) |> mutate(dosing = "given"),
    as.data.frame(o_nca_unaware) |> mutate(dosing = "not given")
  ) |>
  filter(PPTESTCD %in% c("aucint.last", "aucint.inf.obs"))

d_compare |> select(dosing, PPTESTCD, PPORRES)
# A tibble: 4 × 3
  dosing    PPTESTCD       PPORRES
  <chr>     <chr>            <dbl>
1 given     aucint.last       24.4
2 given     aucint.inf.obs    30.6
3 not given aucint.last       34.6
4 not given aucint.inf.obs    34.6
# The method column records how each value was calculated
d_compare$PPANMETH
[1] "AUC: lin up/log down. Interpolation: dose-aware. Extrapolation: AUClast"                  
[2] "AUC: lin up/log down. Interpolation: dose-aware. Extrapolation: half-life"                
[3] "AUC: lin up/log down. Interpolation: not dose-aware (no dosing data). Extrapolation: none"
[4] "AUC: lin up/log down. Interpolation: not dose-aware (no dosing data). Extrapolation: none"

With the doses given, aucint.last stops at Tlast (8 h; 24.4), and aucint.inf.obs extrapolates from 8 to 12 h along the terminal slope (30.6). Without the doses, PKNCA cannot know that the 12.5-hour sample follows a new dose, so both parameters interpolate from the 8-hour sample up toward it and overestimate the area (34.6).

5.2.2 Interval AUMC (aumcint.*) (≥ 0.13.0)

The aumcint.* family mirrors aucint.* for the first-moment curve, with the same dose-aware rules: aumcint.last, aumcint.all, aumcint.inf.obs, and aumcint.inf.pred.

# The two-dose profile above, with the missing 12-hour trough
aumcint_window <- data.frame(
  start            = 0,
  end              = 12,
  aucint.last      = TRUE,
  aumcint.last     = TRUE,
  aumcint.inf.obs  = TRUE
)

o_nca_aumcint <- pk.nca(PKNCAdata(o_conc_md, o_dose_md, intervals = aumcint_window))
as.data.frame(o_nca_aumcint) |>
  filter(grepl("^au.*int", PPTESTCD)) |>
  select(start, end, PPTESTCD, PPORRES)
# A tibble: 3 × 4
  start   end PPTESTCD        PPORRES
  <dbl> <dbl> <chr>             <dbl>
1     0    12 aucint.last        24.4
2     0    12 aumcint.last       92.7
3     0    12 aumcint.inf.obs   153. 

As with the AUC, aumcint.last stops at Tlast (8 h) and aumcint.inf.obs extrapolates the moment curve to 12 h along the terminal slope. The interval MRT and IV forms of these parameters (aumcivint.* and the rest of the aumciv* family) are covered in the IV dosing chapter.


5.3 Concentration interpolation at interval boundaries

When a requested interval boundary (e.g. end = 5) falls between two observed timepoints, only the aucint.* (and aumcint.*) parameters estimate the concentration at that boundary — handled internally by interp.extrap.conc(), or by interp.extrap.conc.dose() when dosing data are given. auclast and the other non-interval AUCs do not interpolate: they integrate only the samples inside the interval, up to the last measurable one.

The interpolation method matches the AUC method:

  • "linear" → linear interpolation
  • "lin-log" → linear interpolation before Tmax, log-linear after Tmax
  • "lin up/log down" → log-linear interpolation on the descending phase
# end = 5 falls between observed samples t = 3.82 and t = 5.10 for subject 1
interp_interval <- data.frame(start = 0, end = 5, auclast = TRUE, aucint.last = TRUE)
o_nca_interp <- pk.nca(PKNCAdata(o_conc, o_dose, intervals = interp_interval))
as.data.frame(o_nca_interp) |>
  filter(PPTESTCD %in% c("auclast", "aucint.last")) |>
  select(start, end, PPTESTCD, PPORRES)
# A tibble: 2 × 4
  start   end PPTESTCD    PPORRES
  <dbl> <dbl> <chr>         <dbl>
1     0     5 auclast        32.1
2     0     5 aucint.last    42.1

auclast stops at the 3.82-hour sample (32.1), while aucint.last adds the area from 3.82 h to the interpolated concentration at 5 h (42.1).

When dosing data are given, the aucint.* and aumcint.* parameters estimate their boundaries with dose-aware interpolation instead, so a boundary at a dose time takes the concentration from the correct side of the dose — see the Dose-aware interpolation chapter.


5.4 Percent extrapolation (%AUCextrap)

When using aucinf, you should check what fraction was extrapolated beyond the last observation. A high aucpext means the terminal phase was poorly characterized.

pext_interval <- data.frame(
  start        = 0,
  end          = Inf,
  auclast      = TRUE,
  aucinf.obs   = TRUE,
  aucpext.obs  = TRUE,
  aucpext.pred = TRUE
)

o_nca_pext <- pk.nca(PKNCAdata(o_conc, o_dose, intervals = pext_interval))
as.data.frame(o_nca_pext) |>
  filter(grepl("^auc", PPTESTCD)) |>
  select(PPTESTCD, PPORRES)
# A tibble: 5 × 2
  PPTESTCD     PPORRES
  <chr>          <dbl>
1 auclast        147. 
2 aucinf.obs     215. 
3 aucinf.pred    215. 
4 aucpext.obs     31.5
5 aucpext.pred    31.5

Here about 31.5% of aucinf.obs is extrapolated — well above the conventional 20% acceptance limit. Note that pk.nca() computes aucpext but does not flag anything on its own: the exclude column (not shown above) is all NA despite the high extrapolation. Flagging is a manual step with exclude() after pk.nca():

o_nca_pext_excl <- exclude(
  o_nca_pext,
  reason = "aucpext.obs > 20%",
  FUN    = exclude_nca_max.aucinf.pext()
)

# Show the flagged rows
as.data.frame(o_nca_pext_excl) |>
  filter(!is.na(exclude)) |>
  select(PPTESTCD, PPORRES, exclude)
# A tibble: 4 × 3
  PPTESTCD     PPORRES exclude     
  <chr>          <dbl> <chr>       
1 aucinf.obs     215.  aucpext > 20
2 aucinf.pred    215.  aucpext > 20
3 aucpext.obs     31.5 aucpext > 20
4 aucpext.pred    31.5 aucpext > 20

The aucinf results and the parameters derived from them (including aucpext itself) are now flagged. Because FUN supplies its own exclusion text, the recorded reason is the function’s "aucpext > 20" rather than the reason = string.

The 20% default comes from the max.aucinf.pext option — but only the global option: called with no argument, exclude_nca_max.aucinf.pext() reads PKNCA.options("max.aucinf.pext"). A per-object setting such as PKNCAdata(options = list(max.aucinf.pext = 10)) never reaches the exclusion function (and does not make pk.nca() flag anything by itself). To flag at a different threshold, pass it to the function directly: exclude_nca_max.aucinf.pext(10).


5.5 Integration methods

The integration method controls how the area of each trapezoid is computed between consecutive timepoints. Each rule, including the zero-concentration fallbacks, is derived in the AUC integration methods vignette.

# Numeric comparison of the three methods on one subject
methods <- c("linear", "lin-log", "lin up/log down")

results <- lapply(methods, function(m) {
  o <- pk.nca(PKNCAdata(o_conc, o_dose,
                        intervals = data.frame(start=0, end=Inf, auclast=TRUE),
                        options   = list(auc.method = m)))
  as.data.frame(o) |>
    filter(PPTESTCD == "auclast") |>
    mutate(method = m)
}) |> bind_rows()

results |> select(method, PPORRES) |>
  rename(auclast = PPORRES)
# A tibble: 3 × 2
  method          auclast
  <chr>             <dbl>
1 linear             149.
2 lin-log            147.
3 lin up/log down    147.

5.5.1 Method details

Linear trapezoidal ("linear")

Uses the standard trapezoidal rule throughout: \[\text{AUC}_{t_i \to t_{i+1}} = \frac{(C_i + C_{i+1})}{2} \times (t_{i+1} - t_i)\]

Overestimates during the descending phase (gives too much weight to the higher concentration).

Lin-log ("lin-log")

Uses linear integration from start up to Tmax, and log-linear integration from Tmax onward: \[\text{AUC}_{t_i \to t_{i+1}} = \frac{(C_i - C_{i+1})}{\ln C_i - \ln C_{i+1}} \times (t_{i+1} - t_i) \quad \text{(post-Tmax)}\]

The switch point is Tmax (not whether concentrations are rising or falling), so any post-Tmax secondary peak is integrated log-linearly even if concentrations are rising. Falls back to linear for any interval where either endpoint is zero. Not recommended — "lin up/log down" is almost always preferable as it switches on actual direction rather than Tmax.

Lin up / log down ("lin up/log down") — default

Hybrid: linear trapezoidal on the ascending phase (when \(C_{i+1} \geq C_i\)), log-linear on the descending phase (when \(C_{i+1} < C_i\)). Best of both worlds for most PK profiles.

# Profile with AUClast shaded
d_plot <- d_one |>
  filter(conc > 0) |>
  arrange(time)

ggplot(d_plot, aes(x = time, y = conc)) +
  geom_line(colour = "steelblue", linewidth = 1) +
  geom_point(size = 3, colour = "steelblue") +
  geom_area(alpha = 0.15, fill = "steelblue") +
  labs(title = "Concentration-time profile (shaded area = AUClast)",
       x = "Time (h)", y = "Concentration (mg/L)") +
  theme_minimal()

5.5.2 When to use which method

Study type Recommended method Notes
Most oral/extravascular "lin up/log down" (default) Linear on ascending phase, log-linear on declining phase
IV bolus "lin up/log down" (default) Profile is entirely declining, so log-linear is used throughout — equivalent to "lin-log" for pure bolus but without the Tmax ambiguity
IV infusion with smooth decline "lin up/log down" Concentrations rise during infusion (linear) then fall after end of infusion (log-linear)
Regulatory (some agencies) "linear" Some health authorities require the linear trapezoidal rule throughout
Highly irregular profiles with zeros "linear" Zeros alone are handled per-segment by the log methods (they fall back to linear for those segments); choose "linear" when you want the same rule everywhere, e.g. for some regulatory templates
Never recommended "lin-log" Switches at Tmax rather than on direction of change — gives wrong method for post-Tmax secondary peaks

5.6 AUMC — area under the first moment curve

The area under the first moment curve (AUMC) weights concentrations by time and is used to compute mean residence time (MRT): \[\text{AUMC} = \int_0^{t_{\mathrm{last}}} t \cdot C(t) \, dt\]

Parameter Meaning
aumclast AUMC from 0 to last measurable concentration
aumcall AUMC from 0 to last, treating BLQ as 0
aumcinf.obs AUMC extrapolated to ∞, observed Clast
aumcinf.pred AUMC extrapolated to ∞, predicted Clast
aumcint.last, aumcint.all, aumcint.inf.obs, aumcint.inf.pred (≥ 0.13.0) AUMC over a time window, dose-aware (see Interval AUMC)
aumcivlast, aumcivall, aumcivinf.obs, aumcivinf.pred, aumcivint.last, aumcivint.all (≥ 0.13.0) IV bolus AUMC with C0 back-extrapolation (see the IV dosing chapter)
aumc_interval <- data.frame(
  start         = 0,
  end           = Inf,
  aumclast      = TRUE,
  aumcall       = TRUE,
  aumcinf.obs   = TRUE,
  aumcinf.pred  = TRUE
)

o_nca_aumc <- pk.nca(PKNCAdata(o_conc, o_dose, intervals = aumc_interval))

as.data.frame(o_nca_aumc) |>
  filter(grepl("^aumc", PPTESTCD)) |>
  select(PPTESTCD, PPORRES)
# A tibble: 4 × 2
  PPTESTCD     PPORRES
  <chr>          <dbl>
1 aumclast       1499.
2 aumcall        1499.
3 aumcinf.obs    4546.
4 aumcinf.pred   4546.

5.7 Other interval parameters

These interval parameters are requested the same way as AUCs and often accompany them.

5.7.1 AUC above a threshold (aucabove.*)

aucabove.* computes the area under the curve above a reference concentration — useful for quantifying exposure above a minimum effective concentration (for the duration above it, see time_above).

Parameter Reference concentration
aucabove.trough.all Ctrough (concentration at end of interval)
aucabove.predose.all Cpredose (concentration at interval start)
# ctrough requires an observation exactly at the interval end —
# subject 1's last sample is at t = 24.37 (end = 24 would give ctrough = NA)
above_interval <- data.frame(
  start              = 0,
  end                = 24.37,
  aucabove.trough.all  = TRUE,
  aucabove.predose.all = TRUE,
  ctrough              = TRUE,
  cstart               = TRUE
)

o_nca_above <- pk.nca(PKNCAdata(o_conc, o_dose, intervals = above_interval))

as.data.frame(o_nca_above) |>
  filter(PPTESTCD %in% c("aucabove.trough.all", "aucabove.predose.all", "ctrough", "cstart")) |>
  select(PPTESTCD, PPORRES)
# A tibble: 4 × 2
  PPTESTCD             PPORRES
  <chr>                  <dbl>
1 ctrough                 3.28
2 cstart                  0.74
3 aucabove.predose.all  129.  
4 aucabove.trough.all    69.3 

5.7.2 Time above a threshold (time_above)

time_above computes the total duration (in time units) for which the concentration exceeds a specified threshold. The threshold is set via the conc_above column in the intervals data frame.

# Time above 2 mg/L
time_above_interval <- data.frame(
  start      = 0,
  end        = Inf,
  time_above = TRUE,
  conc_above = 2       # threshold concentration (same units as your data)
)

o_nca_ta <- pk.nca(PKNCAdata(o_conc, o_dose, intervals = time_above_interval))

as.data.frame(o_nca_ta) |>
  filter(PPTESTCD == "time_above") |>
  select(PPTESTCD, PPORRES)
# A tibble: 1 × 2
  PPTESTCD   PPORRES
  <chr>        <dbl>
1 time_above    24.2

5.7.3 Total dose (totdose)

totdose is the total amount administered within the analysis interval — it is extracted from the dose object rather than computed from concentrations. Useful as a reference column in your results data frame.

totdose_interval <- data.frame(
  start    = 0,
  end      = Inf,
  totdose  = TRUE,
  auclast  = TRUE
)

o_nca_td <- pk.nca(PKNCAdata(o_conc, o_dose, intervals = totdose_interval))

as.data.frame(o_nca_td) |>
  filter(PPTESTCD %in% c("totdose", "auclast")) |>
  select(PPTESTCD, PPORRES)
# A tibble: 2 × 2
  PPTESTCD PPORRES
  <chr>      <dbl>
1 auclast     147.
2 totdose     320.

5.7.4 Cav over a specific interval (cav.int.*)

cav.int.* computes the average concentration over the requested interval, without requiring a full dosing-interval design. The numerator is the corresponding aucint.* flavor computed over the window; the divisor is always end − start.

Warning: because the divisor is always end − start, requesting any cav.int.* parameter with end = Inf divides a finite AUC by infinity and reports 0 — always use a finite end.

Parameter Numerator (aucint.* flavor over the window)
cav.int.last aucint.last — stops accumulating area at the last measurable concentration
cav.int.all aucint.all — post-Tlast BLQ values treated as 0
cav.int.inf.obs aucint.inf.obs — λz extrapolation beyond Tlast, observed Clast
cav.int.inf.pred aucint.inf.pred — λz extrapolation beyond Tlast, predicted Clast
# Two finite windows: 0–24 h lies inside the observed profile (Tlast = 24.37 h),
# 0–48 h extends past it
cav_interval <- data.frame(
  start             = 0,
  end               = c(24, 48),
  cav.int.last      = TRUE,
  cav.int.all       = TRUE,
  cav.int.inf.obs   = TRUE,
  cav.int.inf.pred  = TRUE
)

o_nca_cav <- pk.nca(PKNCAdata(o_conc, o_dose, intervals = cav_interval))

as.data.frame(o_nca_cav) |>
  filter(grepl("^cav.int", PPTESTCD)) |>
  select(start, end, PPTESTCD, PPORRES)
# A tibble: 8 × 4
  start   end PPTESTCD         PPORRES
  <dbl> <dbl> <chr>              <dbl>
1     0    24 cav.int.last        6.08
2     0    24 cav.int.all         6.08
3     0    24 cav.int.inf.obs     6.08
4     0    24 cav.int.inf.pred    6.08
5     0    48 cav.int.last        3.07
6     0    48 cav.int.all         3.07
7     0    48 cav.int.inf.obs     4.03
8     0    48 cav.int.inf.pred    4.03

Over 0–24 h all four flavors integrate the same observed curve, so they coincide. Over 0–48 h they separate: the .inf forms extrapolate the tail beyond Tlast with λz (differing from each other only through the observed vs. predicted Clast — nearly identical here), while .last and .all stop accumulating area at Tlast yet still divide by the full 48 h, which is why their average drops from 6.08 to 3.07 mg/L.