Piperacillin (Thorsted 2019)
Source:vignettes/articles/Thorsted_2019_piperacillin.Rmd
Thorsted_2019_piperacillin.RmdModel and source
- Citation: Thorsted A, Kristoffersson AN, Maarbjerg SF, Schroder H, Wang M, Brock B, Nielsen EI, Friberg LE. (2019). Population pharmacokinetics of piperacillin in febrile children receiving cancer chemotherapy: the impact of body weight and target on an optimal dosing regimen. J Antimicrob Chemother 74(10):2984-2993. doi:10.1093/jac/dkz270. Erratum: J Antimicrob Chemother 2020;75(1):254-255, doi:10.1093/jac/dkz429 (corrects a Results sentence and Figure 5 only; no model parameter changed).
- Description: Two-compartment IV population PK model for UNBOUND (free, ultrafiltrate) piperacillin in febrile children aged 1-18 years receiving cancer chemotherapy (Thorsted 2019). Body weight enters every clearance and volume through fixed-exponent allometry (0.75 on CL and Q, 1.0 on Vc and Vp) referenced to 70 kg. Unexplained variability in clearance is carried entirely as inter-occasion variability between febrile episodes (up to four episodes per child); inter-individual variability became insignificant once the between-episode term was added and is not part of the final model. Proportional residual error.
- Article: https://doi.org/10.1093/jac/dkz270 (open access, PMC6916132)
Population
Thorsted et al. 2019 studied 43 children (aged 1-18 years, median 12) with cancer and chemotherapy-induced fever at the Department of Pediatric Oncology, Aarhus University Hospital, Denmark (April 2016 - January 2018). Children could contribute more than one febrile episode, and 89 episodes (1-4 per child) were analysed. Piperacillin/tazobactam (8:1) was given as a 5-minute IV infusion approximately every 8 h at 300 mg/kg/day piperacillin, capped at the adult 16000 mg/day. The 482 serum samples (19 below the 0.5 mg/L LLOQ, handled with the M3 method) were assayed for unbound piperacillin by UPLC after ultrafiltration, so the model describes free concentrations directly. Episode-level body weight had median 39.4 kg (range 9.5-107) and the Schwartz-estimated GFR a median of 172.4 mL/min/1.73 m^2, i.e. a hyperfiltrating population; 63% of the children were male, neutropenia was present in 87% of episodes and bacteraemia in 11% (Table 1).
Source trace
| Model element | Value | Source location |
|---|---|---|
| Two-compartment disposition, first-order elimination from central | – | Results, ‘Pharmacokinetic modelling’ (dOFV = -18.7 vs one-compartment) |
lcl |
log(15.4 L/h) at 70 kg | Table 2 |
lvc |
log(16.0 L) at 70 kg | Table 2 |
lq |
log(0.237 L/h) at 70 kg | Table 2 |
lvp |
log(3.40 L) at 70 kg | Table 2 |
e_wt_cl, e_wt_q
|
fixed 0.75 | Methods ‘Pharmacokinetic modelling’; Table 2 footnote b |
e_wt_vc, e_wt_vp
|
fixed 1.0 | Methods ‘Pharmacokinetic modelling’; Table 2 footnote b |
| Allometric reference weight | 70 kg | Table 2 footnote b |
etaiov_cl_1 … etaiov_cl_4
|
log(1 + 0.166^2) = 0.027183 | Table 2 ‘CV%CL’ 16.6%, inter-fever-episode variability |
| No IIV on any parameter | – | Results: IIV ‘became insignificant’ once IOV was added |
propSd |
0.332 | Table 2 ‘CV%ERR’ 33.2% |
| Occasion = febrile episode, 1-4 per child | – | Methods ‘Pharmacokinetic modelling’; Results ‘Patient characteristics’ |
Model
mod <- readModelDb("Thorsted_2019_piperacillin")
mod_typical <- rxode2::zeroRe(mod)
#> Warning: some etas defaulted to non-mu referenced, possible parsing error: etaiov_cl_1, etaiov_cl_2, etaiov_cl_3, etaiov_cl_4
#> as a work-around try putting the mu-referenced expression on a simple line
#> Warning: some etas defaulted to non-mu referenced, possible parsing error: etaiov_cl_1, etaiov_cl_2, etaiov_cl_3, etaiov_cl_4
#> as a work-around try putting the mu-referenced expression on a simple lineClosed-form half-lives
The paper reports distribution and terminal half-lives of 0.708 h and 10.1 h for a 70-kg patient. These follow exactly from the Table 2 parameters through the two-compartment eigenvalues, so this is a tight transcription gate.
cl <- 15.4
vc <- 16.0
q <- 0.237
vp <- 3.40
k10 <- cl / vc
k12 <- q / vc
k21 <- q / vp
s <- k10 + k12 + k21
alpha <- (s + sqrt(s^2 - 4 * k10 * k21)) / 2
beta <- (s - sqrt(s^2 - 4 * k10 * k21)) / 2
hl <- c(alpha = log(2) / alpha, beta = log(2) / beta)
knitr::kable(
data.frame(
"Half-life" = c("t1/2,alpha (h)", "t1/2,beta (h)"),
Paper = c(0.708, 10.1),
Model = signif(hl, 3),
check.names = FALSE
)
)| Half-life | Paper | Model | |
|---|---|---|---|
| alpha | t1/2,alpha (h) | 0.708 | 0.708 |
| beta | t1/2,beta (h) | 10.100 | 10.100 |
Typical concentration-time courses (Figure 3)
Figure 3 shows typical free concentrations for 15, 40 and 65 kg children given 300 mg/kg/day (capped at 16000 mg/day) as (a) three 5-minute infusions, (b) three 3-hour infusions, and (c) a continuous infusion.
wts <- c(15, 40, 65)
daily_dose <- function(wt) min(300 * wt, 16000)
make_regimen <- function(wt, regimen) {
dd <- daily_dose(wt)
ev <- switch(regimen,
"SI 5 min q8h" = rxode2::et(amt = dd / 3, dur = 5 / 60, ii = 8, addl = 2, cmt = "central"),
"EI 3 h q8h" = rxode2::et(amt = dd / 3, dur = 3, ii = 8, addl = 2, cmt = "central"),
"CI" = rxode2::et(amt = dd, dur = 24, cmt = "central")
)
ev <- rxode2::et(ev, seq(0, 24, by = 0.05), cmt = "central")
d <- as.data.frame(ev)
d$WT <- wt
d$OCC <- 1
d$id <- 1
d
}
grid <- expand.grid(wt = wts, regimen = c("SI 5 min q8h", "EI 3 h q8h", "CI"), stringsAsFactors = FALSE)
fig3 <- Map(function(wt, regimen) {
s <- rxode2::rxSolve(mod_typical, make_regimen(wt, regimen), returnType = "data.frame")
data.frame(time = s$time, Cc = s$Cc, wt = paste(wt, "kg"), regimen = regimen)
}, grid$wt, grid$regimen) |>
dplyr::bind_rows() |>
dplyr::mutate(regimen = factor(regimen, levels = c("SI 5 min q8h", "EI 3 h q8h", "CI")))
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
ggplot(fig3, aes(time, Cc, colour = wt)) +
geom_line() +
geom_hline(yintercept = c(0.5, 2, 4, 16), linetype = "dashed", colour = "grey50") +
facet_wrap(~regimen) +
scale_y_log10() +
scale_x_continuous(breaks = seq(0, 24, 8)) +
labs(
x = "Time (h)", y = "Unbound piperacillin (mg/L)", colour = "Body weight",
caption = "Replicates Figure 3 of Thorsted 2019 (typical patient, 300 mg/kg/day)."
)
#> Warning in scale_y_log10(): log-10 transformation introduced infinite values.
Consistent with the paper, lighter children reach lower troughs because the allometric exponent on CL (0.75) is smaller than on volume (1.0), which shortens their half-life. After the 5-minute infusion the peaks sit in the few hundred mg/L range (about 340-410 mg/L in the first interval; the paper quotes roughly 500 mg/L, read by eye from its log-scale Figure 3).
peaks <- fig3 |>
dplyr::filter(regimen == "SI 5 min q8h", time <= 8) |>
dplyr::group_by(wt) |>
dplyr::summarise(Cmax = max(Cc), C8 = Cc[which.min(abs(time - 8))], .groups = "drop")
knitr::kable(peaks, digits = 2)| wt | Cmax | C8 |
|---|---|---|
| 15 kg | 402.58 | 0.25 |
| 40 kg | 409.88 | 0.35 |
| 65 kg | 338.82 | 0.39 |
Dose required for target attainment (Figure 5)
The corrected Figure 5 (erratum doi:10.1093/jac/dkz429) prints, for typical 15, 40 and 65 kg children at MIC = 4 mg/L (the MIC90), the steady-state dose required for each target together with the resulting AUCss and Cmax,ss, for (a) the licensed regimen (30-minute infusion q6h) and (b) a 3-hour extended infusion q8h.
fig5_ref <- data.frame(
regimen = rep(c("(a) 30 min q6h", "(b) EI 3 h q8h"), each = 6),
target = rep(rep(c("50% fT>4xMIC", "100% fT>MIC"), each = 3), 2),
wt = rep(c(15, 40, 65), 4),
dose_mg = c(2353, 2803, 3459, 7078, 9254, 10299, 1203, 1984, 2652, 10095, 13800, 14625),
auclast = c(518.5, 296.1, 237.9, 1560, 977.3, 708.4, 248.5, 196.6, 182.4, 2087, 1365, 1007),
cmax = c(526.9, 251.4, 185.2, 1585, 829.7, 551.3, 81.1, 63.3, 57.3, 680.4, 439.2, 316.4)
)
fig5_ref$treatment <- paste(fig5_ref$regimen, fig5_ref$target, paste(fig5_ref$wt, "kg"), sep = " | ")Because the model is linear, the steady-state profile scales with dose, so the required dose is found by bisection on one steady-state profile per weight and regimen. The threshold is 16 mg/L for 50% fT>4xMIC and 4 mg/L for 100% fT>MIC.
ss_profile <- function(wt, tau, tinf, amt = 100 * wt, n = 40, by = 0.005) {
ev <- rxode2::et(amt = amt, dur = tinf, ii = tau, addl = n - 1, cmt = "central")
ev <- rxode2::et(ev, seq((n - 1) * tau, n * tau, by = by), cmt = "central")
d <- as.data.frame(ev)
d$WT <- wt
d$OCC <- 1
d$id <- 1
s <- rxode2::rxSolve(mod_typical, d, returnType = "data.frame")
s <- s[s$time >= (n - 1) * tau, ]
data.frame(time = s$time - (n - 1) * tau, Cc = s$Cc, c_per_mg = s$Cc / amt)
}
required_dose <- function(p, threshold, frac) {
ft <- function(dose) mean(p$c_per_mg * dose > threshold)
lo <- 1
hi <- 1e6
for (i in 1:60) {
mid <- sqrt(lo * hi)
if (ft(mid) >= frac) hi <- mid else lo <- mid
}
hi
}
regimens <- list(
"(a) 30 min q6h" = c(tau = 6, tinf = 0.5),
"(b) EI 3 h q8h" = c(tau = 8, tinf = 3)
)
thresholds <- list("50% fT>4xMIC" = c(thr = 16, frac = 0.5), "100% fT>MIC" = c(thr = 4, frac = 1))
fig5_sim <- fig5_ref[, c("regimen", "target", "wt", "treatment")]
fig5_sim$dose_mg <- NA_real_
for (i in seq_len(nrow(fig5_sim))) {
r <- regimens[[fig5_sim$regimen[i]]]
th <- thresholds[[fig5_sim$target[i]]]
p <- ss_profile(fig5_sim$wt[i], r[["tau"]], r[["tinf"]])
fig5_sim$dose_mg[i] <- required_dose(p, th[["thr"]], th[["frac"]])
}
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
dose_cmp <- merge(
fig5_ref[, c("treatment", "dose_mg")], fig5_sim[, c("treatment", "dose_mg")],
by = "treatment", suffixes = c("_paper", "_model")
)
dose_cmp$pct_diff <- 100 * (dose_cmp$dose_mg_model / dose_cmp$dose_mg_paper - 1)
dose_cmp |>
dplyr::rename(
"Regimen | target | weight" = treatment,
"Paper dose (mg)" = dose_mg_paper,
"Model dose (mg)" = dose_mg_model,
"% diff" = pct_diff
) |>
knitr::kable(digits = 1)| Regimen | target | weight | Paper dose (mg) | Model dose (mg) | % diff |
|---|---|---|---|
| (a) 30 min q6h | 100% fT>MIC | 15 kg | 7078 | 7604.8 | 7.4 |
| (a) 30 min q6h | 100% fT>MIC | 40 kg | 9254 | 9956.7 | 7.6 |
| (a) 30 min q6h | 100% fT>MIC | 65 kg | 10299 | 10386.4 | 0.8 |
| (a) 30 min q6h | 50% fT>4xMIC | 15 kg | 2353 | 2568.0 | 9.1 |
| (a) 30 min q6h | 50% fT>4xMIC | 40 kg | 2803 | 3094.7 | 10.4 |
| (a) 30 min q6h | 50% fT>4xMIC | 65 kg | 3459 | 3600.6 | 4.1 |
| (b) EI 3 h q8h | 100% fT>MIC | 15 kg | 10095 | 10170.2 | 0.7 |
| (b) EI 3 h q8h | 100% fT>MIC | 40 kg | 13800 | 13901.4 | 0.7 |
| (b) EI 3 h q8h | 100% fT>MIC | 65 kg | 14625 | 14750.4 | 0.9 |
| (b) EI 3 h q8h | 50% fT>4xMIC | 15 kg | 1203 | 1207.5 | 0.4 |
| (b) EI 3 h q8h | 50% fT>4xMIC | 40 kg | 1984 | 2016.5 | 1.6 |
| (b) EI 3 h q8h | 50% fT>4xMIC | 65 kg | 2652 | 2663.5 | 0.4 |
For the extended-infusion regimen (panel b) every model dose is within 2% of the published dose, and the two licensed-regimen doses at 65 kg are within 1% and 4%. At 15 and 40 kg the licensed-regimen doses (panel a) come out 7-10% higher than published. This is not a transcription error in the model: panel a is not internally consistent with panel b under any single set of linear PK parameters. The steady-state AUC over a dosing interval equals dose / CL whatever the regimen, so the paper’s own dose / AUCss ratio recovers the clearance it simulated with:
fig5_ref |>
dplyr::filter(target == "50% fT>4xMIC") |>
dplyr::mutate(cl_implied = dose_mg / auclast, cl_model = 15.4 * (wt / 70)^0.75) |>
dplyr::select(regimen, wt, cl_implied, cl_model) |>
dplyr::rename(
"Regimen" = regimen, "Body weight (kg)" = wt,
"Paper dose / AUCss (L/h)" = cl_implied, "Model CL (L/h)" = cl_model
) |>
knitr::kable(digits = 2)| Regimen | Body weight (kg) | Paper dose / AUCss (L/h) | Model CL (L/h) |
|---|---|---|---|
| (a) 30 min q6h | 15 | 4.54 | 4.85 |
| (a) 30 min q6h | 40 | 9.47 | 10.12 |
| (a) 30 min q6h | 65 | 14.54 | 14.57 |
| (b) EI 3 h q8h | 15 | 4.84 | 4.85 |
| (b) EI 3 h q8h | 40 | 10.09 | 10.12 |
| (b) EI 3 h q8h | 65 | 14.54 | 14.57 |
Panel b implies the Table 2 clearance at every weight, and so does panel a at 65 kg, but at 15 and 40 kg panel a implies a clearance about 6% lower – the same size as the dose gap. The erratum already corrected dose and AUC values in this figure once; the residual inconsistency is recorded here and not tuned away. The Results text quotes panel a as 0.67-1.96 (50% fT>4xMIC) and 1.98-5.78 (100% fT>MIC) times 80 mg/kg.
b <- dose_cmp$pct_diff[grepl("^\\(b\\)", dose_cmp$treatment)]
a65 <- dose_cmp$pct_diff[grepl("^\\(a\\).*65 kg$", dose_cmp$treatment)]
a_all <- dose_cmp$pct_diff[grepl("^\\(a\\)", dose_cmp$treatment)]
# Deterministic typical-value calculations: tight where the paper is
# self-consistent, a documented envelope for the self-inconsistent panel a.
stopifnot(all(abs(b) < 3), all(abs(a65) < 5), all(abs(a_all) < 12))Steady-state exposure with PKNCA (Figure 5)
The typical steady-state interval at each model-derived dose is analysed with PKNCA (one typical profile per regimen, target and weight) and compared with the AUCss and Cmax,ss printed in Figure 5.
conc <- lapply(seq_len(nrow(fig5_sim)), function(i) {
r <- regimens[[fig5_sim$regimen[i]]]
p <- ss_profile(fig5_sim$wt[i], r[["tau"]], r[["tinf"]], amt = fig5_sim$dose_mg[i], by = 0.02)
data.frame(id = i, treatment = fig5_sim$treatment[i], time = p$time, Cc = p$Cc)
}) |>
dplyr::bind_rows() |>
dplyr::filter(!is.na(Cc))
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
#> ℹ omega/sigma items treated as zero: 'etaiov_cl_1', 'etaiov_cl_2', 'etaiov_cl_3', 'etaiov_cl_4'
doses <- data.frame(id = seq_len(nrow(fig5_sim)), treatment = fig5_sim$treatment, time = 0, amt = fig5_sim$dose_mg)
tau_by_id <- ifelse(grepl("^\\(a\\)", fig5_sim$treatment), 6, 8)
intervals <- data.frame(
id = seq_len(nrow(fig5_sim)), treatment = fig5_sim$treatment,
start = 0, end = tau_by_id, auclast = TRUE, cmax = TRUE
)
conc_obj <- PKNCA::PKNCAconc(conc, Cc ~ time | treatment + id)
dose_obj <- PKNCA::PKNCAdose(doses, amt ~ time | treatment + id)
nca <- PKNCA::pk.nca(PKNCA::PKNCAdata(conc_obj, dose_obj, intervals = intervals))
tbl <- nlmixr2lib::ncaComparisonTable(
nca, fig5_ref[, c("treatment", "auclast", "cmax")],
by = "treatment",
units = c(auclast = "mg*h/L", cmax = "mg/L")
)
knitr::kable(tbl)| NCA parameter | treatment | Reference | Simulated | % diff |
|---|---|---|---|---|
| Cmax (mg/L) | (a) 30 min q6h | 50% fT>4xMIC | 15 kg | 527 | 536 | +1.6% |
| Cmax (mg/L) | (a) 30 min q6h | 50% fT>4xMIC | 40 kg | 251 | 260 | +3.4% |
| Cmax (mg/L) | (a) 30 min q6h | 50% fT>4xMIC | 65 kg | 185 | 192 | +3.7% |
| Cmax (mg/L) | (a) 30 min q6h | 100% fT>MIC | 15 kg | 1580 | 1590 | +0.1% |
| Cmax (mg/L) | (a) 30 min q6h | 100% fT>MIC | 40 kg | 830 | 836 | +0.8% |
| Cmax (mg/L) | (a) 30 min q6h | 100% fT>MIC | 65 kg | 551 | 554 | +0.4% |
| Cmax (mg/L) | (b) EI 3 h q8h | 50% fT>4xMIC | 15 kg | 81.1 | 81.1 | +0.0% |
| Cmax (mg/L) | (b) EI 3 h q8h | 50% fT>4xMIC | 40 kg | 63.3 | 63.5 | +0.4% |
| Cmax (mg/L) | (b) EI 3 h q8h | 50% fT>4xMIC | 65 kg | 57.3 | 57.4 | +0.1% |
| Cmax (mg/L) | (b) EI 3 h q8h | 100% fT>MIC | 15 kg | 680 | 683 | +0.4% |
| Cmax (mg/L) | (b) EI 3 h q8h | 100% fT>MIC | 40 kg | 439 | 438 | -0.3% |
| Cmax (mg/L) | (b) EI 3 h q8h | 100% fT>MIC | 65 kg | 316 | 318 | +0.4% |
| AUClast (mg*h/L) | (a) 30 min q6h | 50% fT>4xMIC | 15 kg | 518 | 529 | +2.1% |
| AUClast (mg*h/L) | (a) 30 min q6h | 50% fT>4xMIC | 40 kg | 296 | 306 | +3.3% |
| AUClast (mg*h/L) | (a) 30 min q6h | 50% fT>4xMIC | 65 kg | 238 | 247 | +3.9% |
| AUClast (mg*h/L) | (a) 30 min q6h | 100% fT>MIC | 15 kg | 1560 | 1570 | +0.5% |
| AUClast (mg*h/L) | (a) 30 min q6h | 100% fT>MIC | 40 kg | 977 | 984 | +0.7% |
| AUClast (mg*h/L) | (a) 30 min q6h | 100% fT>MIC | 65 kg | 708 | 713 | +0.6% |
| AUClast (mg*h/L) | (b) EI 3 h q8h | 50% fT>4xMIC | 15 kg | 248 | 249 | +0.2% |
| AUClast (mg*h/L) | (b) EI 3 h q8h | 50% fT>4xMIC | 40 kg | 197 | 199 | +1.3% |
| AUClast (mg*h/L) | (b) EI 3 h q8h | 50% fT>4xMIC | 65 kg | 182 | 183 | +0.2% |
| AUClast (mg*h/L) | (b) EI 3 h q8h | 100% fT>MIC | 15 kg | 2090 | 2100 | +0.5% |
| AUClast (mg*h/L) | (b) EI 3 h q8h | 100% fT>MIC | 40 kg | 1360 | 1370 | +0.6% |
| AUClast (mg*h/L) | (b) EI 3 h q8h | 100% fT>MIC | 65 kg | 1010 | 1010 | +0.6% |
Starred rows (> 20% difference) would flag a problem; the AUC and Cmax rows follow the dose comparison above (panel b and 65 kg within a few percent, the self-inconsistent panel a rows at 15 and 40 kg somewhat further off).
res <- as.data.frame(nca$result)
chk <- merge(
res[res$PPTESTCD %in% c("auclast", "cmax"), c("treatment", "PPTESTCD", "PPORRES")],
tidyr::pivot_longer(fig5_ref[, c("treatment", "auclast", "cmax")], -treatment,
names_to = "PPTESTCD", values_to = "ref"
),
by = c("treatment", "PPTESTCD")
)
chk$pct <- 100 * (chk$PPORRES / chk$ref - 1)
stopifnot(
all(abs(chk$pct[grepl("^\\(b\\)", chk$treatment)]) < 5),
all(abs(chk$pct) < 20)
)Variability around the typical profile (Figure 5b)
A stochastic cohort of 200 episodes per weight (IOV on CL, one episode each, proportional residual error excluded as in a prediction interval of the typical profile) reproduces the 90% prediction interval of the 50% fT>4xMIC extended-infusion panel of Figure 5b.
rxode2::rxSetSeed(20190827)
n_per_arm <- 200
n_dose <- 10
tau <- 8
ei50 <- fig5_sim[fig5_sim$regimen == "(b) EI 3 h q8h" & fig5_sim$target == "50% fT>4xMIC", ]
cohort <- lapply(seq_len(nrow(ei50)), function(i) {
ev <- rxode2::et(amt = ei50$dose_mg[i], dur = 3, ii = tau, addl = n_dose - 1, cmt = "central")
ev <- rxode2::et(ev, seq((n_dose - 1) * tau, n_dose * tau, by = 0.1), cmt = "central")
d <- as.data.frame(ev)
ids <- seq_len(n_per_arm) + (i - 1) * n_per_arm
d <- dplyr::bind_rows(lapply(ids, function(id) dplyr::mutate(d, id = id)))
d$WT <- ei50$wt[i]
d$OCC <- 1
d$weight <- paste(ei50$wt[i], "kg")
d
}) |> dplyr::bind_rows()
sim <- rxode2::rxSolve(mod, cohort, keep = "weight", returnType = "data.frame")
#> Warning: some etas defaulted to non-mu referenced, possible parsing error: etaiov_cl_1, etaiov_cl_2, etaiov_cl_3, etaiov_cl_4
#> as a work-around try putting the mu-referenced expression on a simple line
pi_df <- sim |>
dplyr::filter(time >= (n_dose - 1) * tau) |>
dplyr::mutate(tad = time - (n_dose - 1) * tau) |>
dplyr::group_by(weight, tad) |>
dplyr::summarise(med = median(Cc), lo = quantile(Cc, 0.05), hi = quantile(Cc, 0.95), .groups = "drop")
ggplot(pi_df, aes(tad, med)) +
geom_ribbon(aes(ymin = lo, ymax = hi), alpha = 0.3) +
geom_line(linewidth = 1) +
geom_hline(yintercept = 16, linetype = "dashed") +
facet_wrap(~weight) +
scale_y_log10() +
labs(
x = "Time after last dose (h)", y = "Unbound piperacillin (mg/L)",
caption = "Replicates Figure 5b (top row) of Thorsted 2019: median and 90% PI at the dose required for 50% fT>4xMIC."
)
Assumptions and deviations
-
IOV encoding. The paper’s only random effect on a
structural parameter is inter-occasion (between-febrile-episode)
variability on CL, with no IIV in the final model. rxode2 cannot
simulate the
eta ~ var | occsyntax, so the model multiplexes four per-occasion etas through the canonicalOCCcolumn (children contributed 1-4 episodes), with occasions 2-4 fixed to the occasion-1 variance. Because there is no IIV, simulating each episode as a separate subject withOCC = 1(as done above) is statistically equivalent. -
CV to variance. Table 2 reports the IOV as 16.6%
CV; it was converted to a log-scale variance with
log(1 + CV^2). The proportional residual CV of 33.2% is used directly aspropSd. - Allometric exponent on Q. Table 2 footnote b states the 0.75 exponent for “CL”; the Methods state 0.75 applies to clearances in general, so it is also applied to Q.
- Figure 5a self-inconsistency. At 15 and 40 kg the licensed-regimen panel implies (through dose / AUCss) a clearance about 6% below the Table 2 value that panel b and the 65-kg panel a reproduce, and the model-derived required doses are correspondingly 7-10% higher. The model was not adjusted; the gap is reported in the comparison tables above.
- Target attainment definition. fT>threshold is computed on a 0.005-h grid over one steady-state dosing interval of the typical (no IOV) profile; the paper does not state its grid, which may explain the remaining 1-2% differences in panel b.
-
Unbound concentrations.
Ccis the free (ultrafiltrate) serum concentration; no protein-binding correction should be applied when using the model for fT>MIC. - Tazobactam was not measured and is not modelled.
- Erratum (doi:10.1093/jac/dkz429, J Antimicrob Chemother 2020;75:254-255) corrects the Results sentence on the licensed regimen (the 100% fT>MIC range was misprinted as 2.10-9.25 and should read 1.98-5.78 times 80 mg/kg) and some dose / AUCss values in Figure 5. No model parameter or equation is affected. The corrected 1.98-5.78 range is the one checked above. The Discussion’s 182-248 mg*h/L AUC range was not amended by the erratum.
- Appendix S1 (supplementary data) describes the optimal sampling design only and carries no parameters of the final model.