<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://wrlog.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://wrlog.github.io/" rel="alternate" type="text/html" hreflang="en" /><updated>2026-09-25T14:13:09-04:00</updated><id>https://wrlog.github.io/feed.xml</id><title type="html">Wen Rui Tan</title><subtitle>Personal website and portfolio of Wen Rui Tan, PhD candidate, pharmacometrician and data scientist. I work on population PK/PD modelling, model-informed precision dosing, and omics and wearable data in clinical pharmacology.</subtitle><author><name>Wen Rui Tan</name></author><entry><title type="html">Population PK Models of Isoniazid: What Twelve Studies Show</title><link href="https://wrlog.github.io/2026/09/25/Isoniazid-Population-PK-Systematic-Review/" rel="alternate" type="text/html" title="Population PK Models of Isoniazid: What Twelve Studies Show" /><published>2026-09-25T00:00:00-04:00</published><updated>2026-09-25T00:00:00-04:00</updated><id>https://wrlog.github.io/2026/09/25/Isoniazid-Population-PK-Systematic-Review</id><content type="html" xml:base="https://wrlog.github.io/2026/09/25/Isoniazid-Population-PK-Systematic-Review/"><![CDATA[<h2 id="why-we-did-the-review">Why we did the review</h2>

<p>Isoniazid is one of the two main first-line tuberculosis drugs, along with
rifampicin. Its plasma concentrations vary a lot between patients, and low
concentrations of anti-TB drugs have been linked to treatment failure and to
multidrug-resistant TB. Several population PK models of isoniazid had been
published, but nobody had put them side by side. We wanted to compare the
models and see which covariates kept coming up as explaining the variability.</p>

<h2 id="how-we-did-it">How we did it</h2>

<p>We followed PRISMA and searched PubMed and Scopus for population PK analyses of
isoniazid in volunteers or TB patients from 2011 to 2021. Reviews, methodology
papers, non-compartmental analyses, in vitro and animal work, and studies that
reused a previously published model were excluded. From 107 records, 14 went to
full evaluation and 12 studies (published 2013 to 2021) were included.</p>

<p>Together they covered 1,444 people, from 33 to 466 per study, aged 0 to 72.
Four studies included children, eight were adults only, and one of those was in
healthy volunteers. All gave isoniazid orally.</p>

<h2 id="what-we-found">What we found</h2>

<p>Ten of the twelve models were two-compartment and two were one-compartment. One
of the one-compartment models was built from a single sample taken 3 hours
after the dose, which probably isn’t enough data to support a second
compartment. Absorption was handled in different ways: first-order, transit
compartments (mean transit time 0.66 h, range 0.179 to 0.924 h across studies)
or a lag time. Transit compartments describe the physiology of absorption
better than a lag time does.</p>

<p>Mean reported clearance was 20.55 L/h (SD 16.60). Children cleared the drug
more slowly: median 6.5 L/h (range 4.44 to 11.3) against 22.8 L/h (11.4 to 40.5)
in adults. This fits with how NAT2, the enzyme that acetylates isoniazid,
matures. At birth the slow-acetylator phenotype predominates, and the fast
phenotype develops over roughly the first four years of life in children with
the relevant genotypes. Central volume was also smaller in children.</p>

<p>Across the twelve studies, 29 covariates were tested. NAT2 genotype was the
main one, affecting clearance in 11 studies. Creatinine clearance and body mass
index each came up once on clearance, and body weight (two studies), body mass
index and mid-upper arm circumference (one each) on central volume. Most
studies used a mixture model to split patients into acetylator groups.</p>

<p>The NAT2 effect also tracks ethnicity. In a South African study more than 80%
of patients were slow or intermediate acetylators, and in a Tanzanian study 48%
were slow and 48% intermediate. Studies from Singapore and China found mostly
fast and intermediate acetylators, and those two studies reported some of the
highest clearances in the review.</p>

<p>A few gaps showed up. Only one study modelled the main metabolite,
acetylisoniazid, even though acetylation status matters for hepatotoxicity
(slow acetylators are at higher risk). Only one study checked adherence, by
pill count and medication diaries. Every study did basic internal evaluation
such as goodness-of-fit plots and VPCs, five added bootstrap or NPDE, and only
two tested their model on an external dataset.</p>

<h2 id="what-it-means">What it means</h2>

<p>To dose isoniazid well, a patient’s NAT2 genotype and age should be taken into
account, and body weight and BMI matter for dose adjustment. Genotyping isn’t
routine everywhere, so mixture models are a practical way to estimate
acetylator status when genetic data aren’t available. They still need external
validation: one study that did genotype some patients found no significant
difference in Cmax or AUC between slow-intermediate and rapid acetylators.
Larger studies with better sampling would help, and both existing and new
models should be evaluated on external data.</p>

<p>The paper: Tan WR, Sheikh Ghadzi SM, Hyder Ali IA, Harun SN. Systematic review
of population pharmacokinetic models of isoniazid in children and adults with
tuberculosis. <em>Malaysian Journal of Pharmacy</em>. 2022;8(2):1-15.
<a href="https://doi.org/10.52494/DJIQ7058">doi:10.52494/DJIQ7058</a>. It’s free to read on the journal’s site.</p>]]></content><author><name>Wen Rui Tan</name></author><category term="Pharmacometrics" /><summary type="html"><![CDATA[A plain-language summary of our systematic review of published population pharmacokinetic models of isoniazid in children and adults with tuberculosis]]></summary></entry><entry><title type="html">Dosing Piperacillin-Tazobactam in Children During Surgery</title><link href="https://wrlog.github.io/2026/09/25/Piperacillin-Tazobactam-Perioperative-Dosing/" rel="alternate" type="text/html" title="Dosing Piperacillin-Tazobactam in Children During Surgery" /><published>2026-09-25T00:00:00-04:00</published><updated>2026-09-25T00:00:00-04:00</updated><id>https://wrlog.github.io/2026/09/25/Piperacillin-Tazobactam-Perioperative-Dosing</id><content type="html" xml:base="https://wrlog.github.io/2026/09/25/Piperacillin-Tazobactam-Perioperative-Dosing/"><![CDATA[<h2 id="why-we-looked-at-this">Why we looked at this</h2>

<p>Piperacillin-tazobactam (PTZ) is often given to critically ill children as
prophylaxis around surgery, especially abdominal and ear, nose and throat (ENT)
procedures. International guidance for intraoperative dosing in children is
80-100 mg/kg of piperacillin (90-112.5 mg/kg as PTZ) every 2 hours.</p>

<p>That regimen has two problems. One is exposure: an earlier study from our group
found that children who developed piperacillin-associated acute kidney injury
had an average highest trough of 50 mg/L in the first 24 hours, against 10.7
mg/L in those who didn’t. The other is practical. Clinicians who give
antibiotics in the operating room told us that redosing every 2 hours is hard
to keep up with.</p>

<p>About 68% of a piperacillin dose is excreted unchanged by the kidneys, and
surgery can change renal function, blood volume and fluid balance. There were
population PK models for critically ill children in general, but none for
children during and after surgery. So we set out to build one and use it to
find doses that reach a sensible target without the very high concentrations
the current regimen gives.</p>

<h2 id="what-we-did">What we did</h2>

<p>The data came from a larger prospective PK study of beta-lactams in the
pediatric intensive care unit at Cincinnati Children’s Hospital Medical Center.
We included 34 children who had surgery and were getting PTZ every 2-3 hours:
15 liver transplants, 2 total pancreatectomies with islet autotransplantation,
9 other abdominal surgeries and 8 ENT surgeries. Median age was 7.07 years,
median weight 20 kg and median baseline creatinine clearance (bedside Schwartz)
143 mL/min/1.73 m².</p>

<p>Sampling was opportunistic, so no extra blood was drawn. After a PTZ dose we
requested leftover blood from routine clinical samples and measured total and
free piperacillin by HPLC. That gave intraoperative and postoperative samples
for everyone and preoperative samples for children already in the PICU: 286
total and 285 free concentrations, 1 to 19 per child.</p>

<p>We built separate NONMEM models for total and free piperacillin. The total
model was the primary analysis, and the dosing simulations used the free
model, since the efficacy target is defined on free drug.</p>

<h2 id="the-model">The model</h2>

<p>A two-compartment model fit better than one compartment. Weight entered
through allometric scaling, and creatinine clearance was the only other
covariate kept on clearance:</p>

\[CL\ (\mathrm{L/h}) = 7.1 \times \left(\frac{WT}{70}\right)^{0.75} \times \left(\frac{CrCL}{120}\right)^{0.484}\]

<p>Central volume was 17.2 L/70 kg, intercompartmental clearance 3.25 L/h/70 kg
and peripheral volume 4.79 L/70 kg. Between-patient variability was 28.7% on
clearance and 41% on central volume, with a 37.1% proportional residual error.
Age and surgery type were tested and didn’t stay in.</p>

<p>Treating the preoperative and postoperative periods as separate occasions
improved the fit, but once clearance was normalized for weight and renal
function there were no significant differences between the three periods or
between surgery types. Postoperative clearance did vary a lot between children
(2.26 to 14.96 L/h/70 kg).</p>

<p>Clearance in our patients, 7.1 L/h/70 kg, was lower than the 13.4 and 12.6
L/h/70 kg reported in two earlier studies of critically ill children outside
surgery. Kidney injury after surgical stress could explain some of that, and so
could saturation of clearance at high doses: our patients got around 89 mg/kg
every 2-3 hours, while those studies used 75 mg/kg every 6 hours.</p>

<h2 id="simulating-doses">Simulating doses</h2>

<p>We simulated 1,000 virtual patients per regimen in mrgsolve, grouped by
creatinine clearance (five groups, 20-29 up to 130 and above) and weight (four
groups, 10-29 kg up to 70 kg and above). We compared a 30-minute infusion every
2 hours with a 30-minute loading dose followed by a continuous infusion.</p>

<p>Both targets were on free piperacillin and had to hold for the whole surgery:
above 8 mg/L (the CLSI susceptible breakpoint for <em>Enterococcus</em> and
<em>Enterobacterales</em>) and above 32 mg/L (four times that MIC). A regimen passed
if more than 90% of virtual patients met the target.</p>

<p>The current 100 mg/kg regimen gave peaks and troughs far above either target.
The doses that were enough came out much lower:</p>

<table>
  <thead>
    <tr>
      <th>Target</th>
      <th>Every 2 hours</th>
      <th>Loading dose + continuous infusion</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Free PIP &gt; 8 mg/L</td>
      <td>6-15 mg/kg</td>
      <td>10 mg/kg, then 1.0-2.75 mg/kg/h</td>
    </tr>
    <tr>
      <td>Free PIP &gt; 32 mg/L</td>
      <td>25-55 mg/kg</td>
      <td>20 mg/kg, then 3.25-9.25 mg/kg/h</td>
    </tr>
  </tbody>
</table>

<p>Where a child falls in each range depends on their weight and renal function
group, with smaller children and higher creatinine clearance needing more per
kilogram. The figure plots the every-2-hour recommendations from the paper’s
tables next to the guideline range. Even the largest, 55 mg/kg, is well below
80-100 mg/kg.</p>

<p><img src="https://wrlog.github.io/images/posts/pharmacokinetic/pip-q2h-dose-by-crcl.png" alt="Recommended piperacillin doses every 2 hours by creatinine clearance and body weight group, for the 8 mg/L and 32 mg/L targets, compared with the 80-100 mg/kg guideline range" /></p>

<h2 id="what-it-means-for-dosing">What it means for dosing</h2>

<p>Both approaches reach the targets with much less drug, but I’d lean toward
continuous infusion. For the higher target, every-2-hour dosing still left most
simulated patients with steady-state troughs above 50 mg/L, the level we had
seen in children who developed kidney injury. A continuous infusion keeps
concentrations nearer the target and removes the 2-hour redosing problem.</p>

<p>For the lower target we picked a 10 mg/kg loading dose even though it
overshoots slightly, because it reaches the target in about 12 minutes compared
with 24 for 5 mg/kg. The higher target gives some margin for tissue
penetration, which we didn’t measure, though the tissues involved in these
surgeries are well vascularized.</p>

<h2 id="limitations">Limitations</h2>

<p>Only seven children (21%) were under 2, so we couldn’t model renal maturation
and the recommendations are for children older than 2. Timing of leftover
samples can be imprecise, and we didn’t account for that, which may affect the
intercompartmental clearance and peripheral volume. Intercompartmental
clearance was poorly estimated anyway (74% RSE), as you’d expect from sparse
data. With 34 patients the study is small, even if we believe it’s the largest
population PK study of prophylactic PTZ in this group, so the regimens need
prospective testing. The model could also support Bayesian dose adjustment
after surgery, which needs its own study.</p>

<p>The paper is open access: Tan WR, Irie K, McIntire C, Torres JL, Jones R,
Gibson A, Mizuno T, Tang Girdwood S. Model-informed dose optimization for
prophylactic piperacillin-tazobactam in perioperative pediatric critically ill
patients. <em>Antimicrob Agents Chemother</em>. 2025;69(3):e0122724.
<a href="https://doi.org/10.1128/aac.01227-24">doi:10.1128/aac.01227-24</a>. The general
simulation workflow is in the earlier post on
<a href="https://wrlog.github.io/2023/09/22/Monte-Carlo-Simulation-for-Dose-Optimization/">Monte Carlo simulation for dose optimization</a>.</p>]]></content><author><name>Wen Rui Tan</name></author><category term="Pharmacometrics" /><summary type="html"><![CDATA[A plain-language summary of our population PK study of prophylactic piperacillin-tazobactam in critically ill children undergoing surgery, and the lower doses and continuous infusions it points to]]></summary></entry><entry><title type="html">Wearable Data and Molecular Phenotypes: Aggregation, Diurnal Structure and Associations</title><link href="https://wrlog.github.io/2026/09/21/Wearable-Data-and-Molecular-Phenotypes/" rel="alternate" type="text/html" title="Wearable Data and Molecular Phenotypes: Aggregation, Diurnal Structure and Associations" /><published>2026-09-21T00:00:00-04:00</published><updated>2026-09-21T00:00:00-04:00</updated><id>https://wrlog.github.io/2026/09/21/Wearable-Data-and-Molecular-Phenotypes</id><content type="html" xml:base="https://wrlog.github.io/2026/09/21/Wearable-Data-and-Molecular-Phenotypes/"><![CDATA[<h2 id="introduction">Introduction</h2>

<p>A wrist-worn device gives you a nearly continuous record of physiology over
months, far denser than any biospecimen schedule. The biology (a metabolomics
panel, a biomarker, a drug concentration) is measured a handful of times. A lot
of the work in linking the two is deciding what the continuous stream means
as a variable before any model sees it.</p>

<p>These notes cover three decisions: how to aggregate a stream into an exposure,
how to line that exposure up with a sparse sample, and what within-day
structure does to an association if you ignore it.</p>

<h2 id="what-a-wearable-measures">What a wearable measures</h2>

<p>Very little coming off a consumer or research-grade wearable is a direct
measurement of what it’s named after.</p>

<ul>
  <li>Steps are an accelerometer signal run through a proprietary step-detection
algorithm. Two devices disagree with each other, and one device can disagree
with itself after a firmware update.</li>
  <li>Sleep stages are inferred from movement and heart rate; this isn’t
polysomnography. Total sleep time is estimated reasonably well, but
stage-level output is much weaker, and I’d treat REM/deep splits as
device-specific constructs rather than physiology.</li>
  <li>Heart rate variability comes from an optical pulse signal, so strictly it’s
pulse rate variability, and it’s sensitive to motion and perfusion. The
reported metric is often RMSSD over a fixed nocturnal window, which is one
choice among several and not a standard.</li>
  <li>Resting heart rate is a daily summary from a vendor algorithm whose
definition of “resting” is undocumented and can change.</li>
</ul>

<p>So a wearable variable is much safer to compare within a device and firmware
version than across them. Either put device model in the model as a covariate
or restrict the cohort to one device. It also helps to think of these as
digital phenotypes: reproducible constructs with some biological meaning, but
not gold-standard measurements. That’s enough for association work but not for
claims about mechanism.</p>

<h2 id="missing-data">Missing data</h2>

<p>A wearable records nothing when it’s off the wrist, and people take it off for
reasons: charging, showering, discomfort, illness, travel, losing interest in
the study. That is missing-not-at-random in the most direct way, because
non-wear is correlated with the states you’re studying.</p>

<p>At minimum, set a wear-time rule in advance. A day counts only if wear time is
above some threshold (10 or 16 hours are common; nocturnal metrics need the
night itself covered), and a participant-period counts only if it has enough
valid days. These thresholds matter, so report how many participant-days, and
which participants, each rule removed. If the cohort goes from 60 participants
to 34 by the time you fit the model without anyone saying so, the analysis is
no longer about the cohort in the methods section.</p>

<p>Check whether valid-day counts differ by group. If they do, the missingness is
informative and the complete-case analysis is biased. State the likely
direction of the bias even if you can’t correct it.</p>

<h2 id="from-stream-to-exposure">From stream to exposure</h2>

<p>The model needs one number per sample and the sensor gives thousands. Most of
the analytic freedom is in this aggregation step, so fix it before looking at
outcomes. There are four choices:</p>

<ol>
  <li>Window: how much time before (or around) the biological sample counts. The
preceding night, 24 hours, 7 days. Match it to the biology’s timescale; a
metabolite with a half-life of hours shouldn’t be regressed on a 7-day
activity average.</li>
  <li>Summary: mean, median, total, time above a threshold, or a variability
measure like the standard deviation across days. This is the same idea as
exposure metrics in PK, where $AUC$, $C_{max}$ and time above target are
three summaries of one curve that answer different questions.</li>
  <li>Reference: the absolute value, or the deviation from the participant’s own
baseline. The deviation is usually better, because between-person
differences in resting heart rate are much bigger than the within-person
changes you care about. That’s what person-mean centring gives you:</li>
</ol>

\[x_{ij}^{\text{within}} = x_{ij} - \bar{x}_{i\cdot},
\qquad
x_{i}^{\text{between}} = \bar{x}_{i\cdot} - \bar{x}_{\cdot\cdot}\]

<p>Putting both terms in separates the within-person effect from the
   between-person one. Using only the raw value estimates a mix of the two and
   reports it as one effect.</p>

<ol>
  <li>Lag: whether the exposure comes before the sample, after it, or around it.
Only “before” supports a directional reading, and only weakly.</li>
</ol>

<p>The risk here is multiplicity that nobody counts. Three windows, four summaries
and two references make 24 versions of the same association, and reporting
whichever one reached significance is the garden of forking paths again.
Pre-specify one primary aggregation. If you run the others, call them a
sensitivity analysis and report where they disagree.</p>

<h2 id="diurnal-structure">Diurnal structure</h2>

<p>Both sides of the association vary over the day. Many plasma metabolites
(cortisol-associated species, bile acids, amino acids after a meal) have
systematic diurnal variation, and so does every wearable metric. If sample
collection time differs between groups, time of day is a confounder that looks
just like an effect.</p>

<p>The cheap fix is to record the clock time of sampling and adjust for it. Two
harmonic terms handle a daily rhythm without using many degrees of freedom:</p>

\[f(t) = \beta_c \cos\!\left(\frac{2\pi t}{24}\right) + \beta_s \sin\!\left(\frac{2\pi t}{24}\right)\]

<p>This is a cosine with amplitude $\sqrt{\beta_c^2 + \beta_s^2}$ and phase
$\arctan(\beta_s / \beta_c)$. It’s linear in its parameters, so any mixed model
can estimate it. A second harmonic ($2\pi t / 12$) picks up a bimodal shape,
such as a second peak after a meal.</p>

<p>Describing diurnal variability is also a result in itself, and often a more
reliable one than the cross-sectional association: which analytes have a
detectable within-day rhythm, how large it is, and its phase relative to sleep
offset. That question actually uses the dense side of the data. A
single-timepoint association mostly doesn’t.</p>

<h2 id="the-model">The model</h2>

<p>With repeated visits per participant, start with a random intercept per
participant:</p>

\[y_{ij} = \beta_0 + \beta_1 x_{ij}^{\text{within}} + \beta_2 x_{i}^{\text{between}}
+ f(t_{ij}) + \mathbf{z}_{ij}^\top \boldsymbol{\gamma} + u_i + \varepsilon_{ij},
\qquad u_i \sim N(0, \sigma_u^2)\]

<p>Here $y_{ij}$ is the log-transformed analyte, $x$ is the aggregated wearable
exposure split into within- and between-person parts, $f(t_{ij})$ are the
diurnal terms, and $\mathbf{z}$ holds covariates (age, sex, BMI, device model,
and season if the study runs long enough).</p>

<p>On interpretation: $\beta_1$ is a within-person effect. It describes what
happens to the analyte when this participant’s activity or sleep moves away
from their own usual level. That’s closer to a causal reading, and stable
between-person traits don’t confound it. With a log-transformed analyte,
$\beta_1$ is multiplicative (a unit change in exposure multiplies the analyte
by $e^{\beta_1}$), so report it that way rather than as a raw coefficient on
an arbitrary intensity scale.</p>

<h2 id="multiple-testing">Multiple testing</h2>

<p>Thousands of analytes crossed with a few wearable metrics is a lot of tests.
Run Benjamini–Hochberg within each wearable metric, not pooled across metrics
or with unrelated contrasts, so the threshold stays interpretable.</p>

<p>Expect small effects. A within-person association between last night’s sleep
duration and a plasma metabolite is a modest multiplicative shift, and a study
powered for a large effect will find nothing. The analytes are also correlated,
often because they sit in the same pathway or are adducts of one compound, so
50 significant features may be far fewer distinct findings. Collapse to
compounds or pathways before counting.</p>

<p>The findings that replicate tend to be the ones that hold across the
sensitivity aggregations, which aren’t necessarily the ones with the smallest
$q$-value in the primary analysis.</p>

<h2 id="summary">Summary</h2>

<p>Linking wearables to molecular data is mostly a problem of defining the
measurement. Work out what the device is reporting, set a wear-time rule and
report what it removes, fix one aggregation window and summary before looking
at outcomes, split within- from between-person variation, adjust for time of
day on both sides, and control error within each metric. The sensor stream is
where most of the information is, and a single cross-sectional correlation
wastes most of it; looking at within-day and within-person structure makes
better use of it. The note on
<a href="https://wrlog.github.io/2026/09/14/Untargeted-Metabolomics-Feature-Tables/">untargeted metabolomics</a>
covers the other side of the join.</p>]]></content><author><name>Wen Rui Tan</name></author><category term="Wearables" /><summary type="html"><![CDATA[Turning continuous sensor streams into analysable exposures, lining them up with sparse biological sampling, and testing metabolite-wearable associations without inflating the result]]></summary></entry><entry><title type="html">Untargeted Metabolomics: From Feature Table to Association List</title><link href="https://wrlog.github.io/2026/09/14/Untargeted-Metabolomics-Feature-Tables/" rel="alternate" type="text/html" title="Untargeted Metabolomics: From Feature Table to Association List" /><published>2026-09-14T00:00:00-04:00</published><updated>2026-09-14T00:00:00-04:00</updated><id>https://wrlog.github.io/2026/09/14/Untargeted-Metabolomics-Feature-Tables</id><content type="html" xml:base="https://wrlog.github.io/2026/09/14/Untargeted-Metabolomics-Feature-Tables/"><![CDATA[<h2 id="introduction">Introduction</h2>

<p>Untargeted metabolomics data usually arrives as a feature table: samples in
rows, mass-spectrometry features in columns, intensities in the cells. It looks
ready for statistics, but most of the choices that decide whether the final
association list means anything are made between that export and the first
model fit, and they often get made without anyone writing them down.</p>

<p>These notes go through those choices in the order they come up. The example I
have in mind is plasma metabolomics from a cohort with repeated visits per
patient, which breaks a few of the usual shortcuts.</p>

<h2 id="what-a-feature-table-is">What a feature table is</h2>

<p>A few things make a feature table different from the tidy assay data most
statistics courses assume.</p>

<p>A feature is not a metabolite. It is a mass-to-charge ratio at a retention
time, and it could be a metabolite, an adduct of one, an in-source fragment, an
isotopologue or contamination. Annotation maps some features to compounds with
varying confidence, and the confidence level matters: a level-1 identification
matched to an authentic standard and a level-3 putative class assignment don’t
support the same claims. Several correlated features are often one compound
showing up more than once.</p>

<p>Intensity is relative. Ionization efficiency differs by orders of magnitude
between compounds, so intensities can be compared across samples within a
feature but not across features. An untargeted run can’t tell you that
metabolite A is more abundant than metabolite B. Fold changes within a feature
are fine, and that’s what the analysis is for anyway.</p>

<p>Blank cells mean the feature wasn’t detected in that sample. It might be
absent, below the limit of detection, or missed by the peak picker. These are
different mechanisms, and how you handle them shapes the result more than which
test you pick.</p>

<h2 id="filtering">Filtering</h2>

<p>Two filters come before normalization. For both, report what the choice cost.</p>

<p>Prevalence: drop features detected in fewer than some fraction of samples. A
common default is 50%, or 50% within at least one group. The within-group
version matters when a compound is present in one arm and absent in the other,
because a global prevalence filter throws away exactly that case.</p>

<p>Annotation: keeping only annotated features makes interpretation possible but
discards real signal. Whether that’s acceptable depends on whether you want a
mechanism or a classifier.</p>

<p>Every filter should log what it dropped and why, and the counts should go in
the write-up. If a pipeline silently removes 60% of the features, versioning
the code doesn’t make it reproducible in any useful sense.</p>

<h2 id="normalization-and-transformation">Normalization and transformation</h2>

<p>Total ion current (TIC) normalization divides each sample by its own summed
intensity, which corrects for injection volume and gross sensitivity drift:</p>

\[\tilde{x}_{ij} = \frac{x_{ij}}{\sum_{k} x_{ik}} \cdot \bar{s}\]

<p>where $x_{ij}$ is the intensity of feature $j$ in sample $i$ and $\bar{s}$ is
the mean sample sum. The catch is that it assumes total signal is constant. If
one group really has more total metabolite content, TIC normalization removes
that effect. Probabilistic quotient normalization is the usual alternative;
median or quantile scaling are simpler options.</p>

<p>Then log-transform. Intensities are right-skewed over several orders of
magnitude, and the log turns multiplicative effects into additive ones, which
is what a linear model can express:</p>

\[y_{ij} = \log_2(\tilde{x}_{ij} + c)\]

<p>The pseudo-count $c$ is only there to handle zeros, and its value decides how
far below the detected range a non-detect ends up. If non-detects are treated
as missing (next section), you don’t need $c$ at all, which is another point in
favour of doing that.</p>

<h2 id="non-detects-treat-as-missing-dont-impute">Non-detects: treat as missing, don’t impute</h2>

<p>The usual approach is to impute non-detects at half the minimum observed
intensity, or by $k$-nearest neighbours, and carry on. Both are fine under
missing-at-random. Metabolomics non-detects are rarely missing at random,
though. They’re missing because the value was low, which is
missing-not-at-random by definition.</p>

<p>Imputing at a fixed low value puts a spike in the left tail of the
distribution. If one group has more non-detects (which is what happens when a
compound differs between groups), the spike lands unevenly and creates a
difference in the mean for the test to find.</p>

<p>What I’d default to instead is two analyses that answer different questions:</p>

<ol>
  <li>Model detected intensity only, with non-detects as missing. Among samples
where the feature is present, does its abundance differ?</li>
  <li>Test presence/absence separately, as a binomial contrast on detection
status. Is the feature detected at different rates between groups?</li>
</ol>

<p>The intensity model can’t see a feature that’s simply absent in one group, and
the detection-rate test can’t see a graded difference, so reporting both keeps
one from being mistaken for the other. If only a handful of samples in a group
have the feature detected, skip the intensity model for that feature instead of
fitting it to four points.</p>

<h2 id="repeated-visits-and-batch-structure">Repeated visits and batch structure</h2>

<p>With several samples per patient the observations aren’t independent. A
patient random intercept is the minimum:</p>

\[y_{ij} = \beta_0 + \beta_1 \, \mathrm{group}_i + \mathbf{z}_i^\top \boldsymbol{\gamma} + u_{p(i)} + \varepsilon_{ij},
\qquad u_p \sim N(0, \sigma_u^2)\]

<p>where $u_{p(i)}$ is the random intercept for the patient contributing sample
$i$ and $\mathbf{z}_i$ holds covariates. Ordinary least squares treats repeat
visits as independent patients and understates the standard error on
$\beta_1$, and the problem is worst when patients contribute unequal numbers of
visits.</p>

<p>Run order and batch need the same attention. LC-MS sensitivity drifts within a
run and shifts between batches. If group correlates with run order, which
happens whenever samples are run as they come in, the drift becomes the group
effect. Two things help: a run-order-adjusted model as a sensitivity check, and
a cross-batch check on whether pooled QC samples behave consistently that halts
the analysis when they don’t.</p>

<p>Fitting OLS and the run-order-adjusted model next to the primary mixed model is
cheap and shows whether a finding depends on the model specification. If the
three disagree, report that.</p>

<h2 id="multiple-testing">Multiple testing</h2>

<p>A few thousand features at $\alpha = 0.05$ gives a few hundred false positives
before any biology. The standard fix is Benjamini–Hochberg control of the false
discovery rate: order the $p$-values $p_{(1)} \le \dots \le p_{(m)}$ and reject
up to the largest $k$ with</p>

\[p_{(k)} \le \frac{k}{m} q\]

<p>Two practical points. Apply it within each contrast rather than pooling every
comparison in the analysis, otherwise the threshold depends on how many
unrelated questions happen to be in the same script. And FDR control is about
the expected proportion of false rejections in the list. A $q$-value of 0.05
doesn’t mean that particular feature has a 5% chance of being wrong.</p>

<p>Adducts and fragments of the same compound make the tests correlated.
Benjamini–Hochberg is valid under positive regression dependence, which is the
usual justification, but correlated features still make the list look longer
than the number of distinct findings. Collapse to compounds before counting
discoveries, so you’re not reporting 40 features that are really 11 molecules.</p>

<h2 id="what-to-report">What to report</h2>

<p>Along with the ranked table, I’d want:</p>

<ul>
  <li>counts at every filter, with what was dropped</li>
  <li>the primary model and its sensitivity comparisons, side by side</li>
  <li>presence/absence tested separately from intensity</li>
  <li>$q$-values within contrast, with discoveries collapsed to compounds</li>
  <li>a statement of what the design can’t answer</li>
</ul>

<p>The last point matters more than it looks. A cohort can have plenty of power to
ask whether the metabolome separates one disease from another and still be
unable to say whether it predicts treatment response before the first dose. For
example, if medication is recorded without start dates, only a handful of
patients may have a plasma sample that provably comes before treatment, and no
modelling fixes that. In that situation the useful output is a feasibility
assessment giving the design and sample size a proper study would need. That
still counts as a result, even without any $p$-values.</p>

<h2 id="summary">Summary</h2>

<p>With untargeted metabolomics, care in the steps before the statistics matters
more than a clever test. Choose filters against their alternatives, treat
non-detects as missing instead of imputing them, put patient structure in the
model, check run order, and control error within each contrast. The earlier
notes on <a href="https://wrlog.github.io/2022/07/09/Transcription/">transcriptomics</a> and
<a href="https://wrlog.github.io/2022/07/12/ATACseq/">chromatin accessibility</a> cover similar
pre-processing for other assays.</p>]]></content><author><name>Wen Rui Tan</name></author><category term="Omics" /><summary type="html"><![CDATA[Steps between an untargeted metabolomics export and a list of associations: filtering, normalization, non-detects, batch structure, repeated visits and error control]]></summary></entry><entry><title type="html">Variational Autoencoder (VAE) for Model-Informed Precision Dosing and Complex Pharmacokinetic Profiles</title><link href="https://wrlog.github.io/2025/08/08/Variational-Autoencoder/" rel="alternate" type="text/html" title="Variational Autoencoder (VAE) for Model-Informed Precision Dosing and Complex Pharmacokinetic Profiles" /><published>2025-08-08T00:00:00-04:00</published><updated>2025-08-08T00:00:00-04:00</updated><id>https://wrlog.github.io/2025/08/08/Variational%20Autoencoder</id><content type="html" xml:base="https://wrlog.github.io/2025/08/08/Variational-Autoencoder/"><![CDATA[<h2 id="introduction">Introduction</h2>

<p>These are notes on using variational autoencoders (VAEs) for two things in
pharmacometrics: model-informed precision dosing (MIPD) based on
patient-specific profiles, and describing PK profiles that conventional
ODE-based compartmental models struggle with.</p>

<p>A conventional PK model makes you pick a structure up front. That structure is
what gives the parameters physiological meaning and what lets the model
extrapolate. A VAE relaxes it and gives up interpretability and extrapolation
in exchange for flexibility. Whether that’s worth it depends on the problem,
and I’ve tried to say below where I think it isn’t.</p>

<h2 id="what-ode-models-do-and-do-not-constrain">What ODE models do and do not constrain</h2>

<p>Compartmental models assume specific functional relationships:</p>

\[\frac{dC}{dt} = -k \cdot C\]

<p>or more complex structures:</p>

\[\frac{dC}{dt} = \frac{Dose \cdot k_a}{V} \cdot e^{-k_a \cdot t} - k_{el} \cdot C\]

<p>The case for flexible models often gets overstated, so a few things this does
not imply:</p>

<ul>
  <li>Multi-exponential decay isn’t a limitation. A linear compartmental system
produces sums of exponentials by construction; that’s what the compartments
are.</li>
  <li>Enterohepatic recirculation and target-mediated disposition aren’t
“non-compartmental”. Both have well-established ODE formulations, and the
TMDD system is written out in an earlier post on this site.</li>
  <li>Irregular sampling isn’t a problem for ODE models. Nonlinear mixed-effects
estimation was built for sparse, unbalanced clinical data. If anything it’s
the other way round: a network that outputs a fixed grid of time points is
what has trouble with irregular sampling.</li>
</ul>

<p>The actual motivation is narrower. ODE models make you commit to a structure
before fitting, and that commitment is what lets the model extrapolate to
doses and populations it hasn’t seen. A flexible model is worth considering
when the structure really is unknown, when the profile shape itself carries
useful information (clustering patients by profile shape, say), or when
there’s enough data to learn the structure instead of assuming it. Those
situations exist but they’re the exception, and the baseline to beat is a
properly specified population PK model.</p>

<h2 id="vae-architecture-for-pk">VAE architecture for PK</h2>

<h3 id="encoder">Encoder</h3>

<p>The encoder maps observed concentration-time data to a latent representation
of patient physiology:</p>

\[q_\phi(z \mid x) = \mathcal{N}(\mu_\phi(x), \sigma_\phi^2(x))\]

<p>where:</p>
<ul>
  <li>$x = {C_{obs}(t_1), C_{obs}(t_2), …, C_{obs}(t_n), \text{covariates}}$ represents observed data</li>
  <li>$z$ is the latent vector encoding patient-specific PK characteristics</li>
  <li>$\mu_\phi(x)$ and $\sigma_\phi(x)$ are neural network outputs parameterizing the latent distribution</li>
</ul>

<p>The idea is that the latent vector picks up patient-specific clearance,
volume of distribution and absorption characteristics, and inter-individual
variability in PK behavior generally.</p>

<h3 id="decoder">Decoder</h3>

<p>The decoder reconstructs concentration-time profiles from the latent
representation:</p>

\[p_\theta(C(t) \mid z, \text{Dose}) = \mathcal{N}(\mu_\theta(z, \text{Dose}), \sigma_\theta^2)\]

<p>and $\mu_\theta(z, \text{Dose})$ can represent profiles that don’t come from
an ODE:</p>

\[C_{pred}(t) = \text{Decoder}(z, \text{Dose}, t)\]

<p>There’s a gap between this expression and the code below, though. Writing
$\text{Decoder}(z, \text{Dose}, t)$ suggests a function of continuous time,
but the network in the code outputs a fixed vector of <code class="language-plaintext highlighter-rouge">time_points</code> values. It
only knows about the grid it was trained on, and it has no idea that
concentration at $t + \Delta t$ is related to concentration at $t$. Neural
ODEs are meant to close that gap (see below).</p>

<p>Within that grid, the decoder isn’t limited to exponential or polynomial
forms. It can learn arbitrary concentration-time curves, multi-modal shapes,
non-standard absorption and complicated elimination phases.</p>

<h3 id="variational-inference">Variational inference</h3>

<p>The latent vector is sampled with the reparameterization trick:</p>

\[z = \mu_\phi(x) + \sigma_\phi(x) \odot \epsilon, \quad \epsilon \sim \mathcal{N}(0, I)\]

<p>which allows gradient-based optimization while keeping the probabilistic
interpretation.</p>

<h2 id="application-1-precision-dosing">Application 1: precision dosing</h2>

<p>For MIPD the VAE learns individual PK characteristics from sparse
observational data.</p>

<p>Training:</p>
<ol>
  <li>Encoder processes observed concentrations: $z_i = \text{Encoder}(C_{obs,i}, \text{covariates}_i)$</li>
  <li>Each patient’s latent vector $z_i$ captures the shape of their PK profile</li>
  <li>Decoder learns to predict full profiles: $C_{pred,i} = \text{Decoder}(z_i, \text{Dose}_i)$</li>
</ol>

<p>Dose optimization: given a target exposure (e.g. an AUC target or trough
concentration), the dose for patient $i$ is</p>

\[\text{Dose}_{optimal,i} = \arg\min_{\text{Dose}} |\text{Target} - \text{Decoder}(z_i, \text{Dose})|\]

<p>What this offers for MIPD: full profiles inferred from a few observations,
individual variability held in the latent space, dose individualization done
directly in that patient-specific space, and prediction intervals from the
probabilistic framework.</p>

<h3 id="implementation-example">Implementation example</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">torch</span>
<span class="kn">import</span> <span class="nn">torch.nn</span> <span class="k">as</span> <span class="n">nn</span>

<span class="k">class</span> <span class="nc">PKVAE</span><span class="p">(</span><span class="n">nn</span><span class="p">.</span><span class="n">Module</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">latent_dim</span><span class="o">=</span><span class="mi">8</span><span class="p">,</span> <span class="n">time_points</span><span class="o">=</span><span class="mi">24</span><span class="p">):</span>
        <span class="nb">super</span><span class="p">().</span><span class="n">__init__</span><span class="p">()</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">latent_dim</span> <span class="o">=</span> <span class="n">latent_dim</span>      <span class="c1"># encode() needs this at call time
</span>        <span class="bp">self</span><span class="p">.</span><span class="n">time_points</span> <span class="o">=</span> <span class="n">time_points</span>
        <span class="c1"># Encoder: concentration-time data -&gt; latent distribution
</span>        <span class="bp">self</span><span class="p">.</span><span class="n">encoder</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Sequential</span><span class="p">(</span>
            <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="n">time_points</span> <span class="o">+</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">64</span><span class="p">),</span>  <span class="c1"># +3 for covariates
</span>            <span class="n">nn</span><span class="p">.</span><span class="n">ReLU</span><span class="p">(),</span>
            <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="mi">64</span><span class="p">,</span> <span class="mi">32</span><span class="p">),</span>
            <span class="n">nn</span><span class="p">.</span><span class="n">ReLU</span><span class="p">(),</span>
            <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="mi">32</span><span class="p">,</span> <span class="n">latent_dim</span> <span class="o">*</span> <span class="mi">2</span><span class="p">)</span>  <span class="c1"># mean and log-variance
</span>        <span class="p">)</span>
        
        <span class="c1"># Decoder: latent + dose -&gt; concentration profile
</span>        <span class="bp">self</span><span class="p">.</span><span class="n">decoder</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Sequential</span><span class="p">(</span>
            <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="n">latent_dim</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">32</span><span class="p">),</span>  <span class="c1"># +1 for dose
</span>            <span class="n">nn</span><span class="p">.</span><span class="n">ReLU</span><span class="p">(),</span>
            <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="mi">32</span><span class="p">,</span> <span class="mi">64</span><span class="p">),</span>
            <span class="n">nn</span><span class="p">.</span><span class="n">ReLU</span><span class="p">(),</span>
            <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="mi">64</span><span class="p">,</span> <span class="n">time_points</span><span class="p">)</span>
        <span class="p">)</span>
    
    <span class="k">def</span> <span class="nf">encode</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">):</span>
        <span class="n">h</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">encoder</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
        <span class="n">mu</span><span class="p">,</span> <span class="n">logvar</span> <span class="o">=</span> <span class="n">h</span><span class="p">[:,</span> <span class="p">:</span><span class="bp">self</span><span class="p">.</span><span class="n">latent_dim</span><span class="p">],</span> <span class="n">h</span><span class="p">[:,</span> <span class="bp">self</span><span class="p">.</span><span class="n">latent_dim</span><span class="p">:]</span>
        <span class="k">return</span> <span class="n">mu</span><span class="p">,</span> <span class="n">logvar</span>
    
    <span class="k">def</span> <span class="nf">reparameterize</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">mu</span><span class="p">,</span> <span class="n">logvar</span><span class="p">):</span>
        <span class="n">std</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">exp</span><span class="p">(</span><span class="mf">0.5</span> <span class="o">*</span> <span class="n">logvar</span><span class="p">)</span>
        <span class="n">eps</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">randn_like</span><span class="p">(</span><span class="n">std</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">mu</span> <span class="o">+</span> <span class="n">eps</span> <span class="o">*</span> <span class="n">std</span>
    
    <span class="k">def</span> <span class="nf">decode</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">z</span><span class="p">,</span> <span class="n">dose</span><span class="p">):</span>
        <span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="n">decoder</span><span class="p">(</span><span class="n">torch</span><span class="p">.</span><span class="n">cat</span><span class="p">([</span><span class="n">z</span><span class="p">,</span> <span class="n">dose</span><span class="p">.</span><span class="n">unsqueeze</span><span class="p">(</span><span class="mi">1</span><span class="p">)],</span> <span class="n">dim</span><span class="o">=</span><span class="mi">1</span><span class="p">))</span>
    
    <span class="k">def</span> <span class="nf">forward</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">dose</span><span class="p">):</span>
        <span class="n">mu</span><span class="p">,</span> <span class="n">logvar</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">encode</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
        <span class="n">z</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">reparameterize</span><span class="p">(</span><span class="n">mu</span><span class="p">,</span> <span class="n">logvar</span><span class="p">)</span>
        <span class="n">recon</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">decode</span><span class="p">(</span><span class="n">z</span><span class="p">,</span> <span class="n">dose</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">recon</span><span class="p">,</span> <span class="n">mu</span><span class="p">,</span> <span class="n">logvar</span>

<span class="k">def</span> <span class="nf">precision_dosing_loss</span><span class="p">(</span><span class="n">recon</span><span class="p">,</span> <span class="n">target</span><span class="p">,</span> <span class="n">mu</span><span class="p">,</span> <span class="n">logvar</span><span class="p">,</span> <span class="n">beta</span><span class="o">=</span><span class="mf">0.1</span><span class="p">):</span>
    <span class="c1"># Both terms sum over the feature axis and average over the batch. Mixing a
</span>    <span class="c1"># mean reconstruction with a summed KL, as is often written, makes beta
</span>    <span class="c1"># depend on batch size and on how many time points you happen to have.
</span>    <span class="n">recon_loss</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">functional</span><span class="p">.</span><span class="n">mse_loss</span><span class="p">(</span>
        <span class="n">recon</span><span class="p">,</span> <span class="n">target</span><span class="p">,</span> <span class="n">reduction</span><span class="o">=</span><span class="s">"none"</span><span class="p">).</span><span class="nb">sum</span><span class="p">(</span><span class="n">dim</span><span class="o">=</span><span class="mi">1</span><span class="p">).</span><span class="n">mean</span><span class="p">()</span>
    <span class="n">kl_loss</span> <span class="o">=</span> <span class="p">(</span><span class="o">-</span><span class="mf">0.5</span> <span class="o">*</span> <span class="p">(</span><span class="mi">1</span> <span class="o">+</span> <span class="n">logvar</span> <span class="o">-</span> <span class="n">mu</span><span class="p">.</span><span class="nb">pow</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">-</span> <span class="n">logvar</span><span class="p">.</span><span class="n">exp</span><span class="p">()).</span><span class="nb">sum</span><span class="p">(</span><span class="n">dim</span><span class="o">=</span><span class="mi">1</span><span class="p">)).</span><span class="n">mean</span><span class="p">()</span>
    <span class="k">return</span> <span class="n">recon_loss</span> <span class="o">+</span> <span class="n">beta</span> <span class="o">*</span> <span class="n">kl_loss</span>
</code></pre></div></div>

<h2 id="application-2-profiles-that-are-hard-to-write-as-odes">Application 2: profiles that are hard to write as ODEs</h2>

<p>Some PK profiles are awkward for standard ODE models.</p>

<p>Multi-phase elimination with non-standard exponents. The traditional form is
$C(t) = A_1 e^{-\lambda_1 t} + A_2 e^{-\lambda_2 t}$; the VAE version is
$C(t) = \text{Decoder}(z, \text{Dose}, t)$, where the decoder learns the
functional form.</p>

<p>Irregular absorption. Complicated lag times, multiple absorption sites or food
effects give profiles that don’t follow a standard $k_a$ model. A decoder can
learn these directly:</p>

\[C_{abs}(t) = \text{Decoder}_{abs}(z_{abs}, t)\]

<p>where $z_{abs}$ encodes patient-specific absorption characteristics.</p>

<p>Target-mediated or saturable processes. When TMDD or another saturable process
causes non-exponential decay, the VAE can capture the profile shape without
explicit Michaelis-Menten equations:</p>

\[C(t) = \text{Decoder}(z, \text{Dose}, t, \text{Target}_{level})\]

<h3 id="objective">Objective</h3>

<p>The VAE objective trades off reconstruction accuracy against regularization
of the latent space:</p>

\[\mathcal{L}_{VAE} = \mathbb{E}_{q_\phi(z|x)}[\log p_\theta(C \mid z, \text{Dose})] - \beta \cdot D_{KL}(q_\phi(z \mid x) \parallel p(z))\]

<p>The first term is the reconstruction term (fit to the observed data), the
second is the KL divergence pulling toward the prior $p(z) = \mathcal{N}(0, I)$,
and $\beta$ weights the two.</p>

<h3 id="comparing-fits">Comparing fits</h3>

\[\text{Reconstruction error} = \frac{1}{N} \sum_{i=1}^{N} ||C_{obs,i} - C_{pred,i}||^2\]

<p>This says nothing about how complex a profile is. A model with enough capacity
pushes it toward zero on the training set however simple the underlying
profile. It’s only informative on held-out patients, and even then a lower
value has to be weighed against how many parameters it took to get there.</p>

<h2 id="neural-odes">Neural ODEs</h2>

<p>A decoder that outputs a fixed grid throws away the one thing we know for sure
about a concentration-time profile: it’s the solution of a differential
equation in time. A neural ODE keeps that structure and only learns the
right-hand side,</p>

\[\frac{d\mathbf{h}}{dt} = f_\theta(\mathbf{h}, t, z), \qquad C(t) = g(\mathbf{h}(t))\]

<p>where $f_\theta$ is a neural network and the profile comes from integrating it
with an ordinary solver. That gets back what the plain decoder loses:</p>

<ul>
  <li>Continuous time. The profile can be evaluated at any $t$, so irregular and
patient-specific sampling works the same way as in a conventional PK model.</li>
  <li>Dosing events. Integration restarts at each dose, so multiple doses and
infusions are handled the way they actually happen instead of being baked
into a fixed output grid.</li>
  <li>Partial mechanism. You can write down the known part of the system and learn
only the unknown part. This hybrid is where I’d start: a one-compartment
model with a learned clearance term is much easier to justify, and much more
identifiable, than a black box over the whole profile.</li>
</ul>

<p>The downsides: neural ODEs train slowly, suffer from whatever stiffness the
solver has trouble with, and overfit easily on sparse clinical data, so they
shouldn’t be the default. But if the reason for dropping ODEs is that the
profile shape is unusual, a neural ODE makes more sense than throwing away the
time structure entirely.</p>

<h2 id="combining-with-conventional-pharmacometrics">Combining with conventional pharmacometrics</h2>

<p>VAEs can also sit alongside ODE models: to classify which patients need a
complex model and which a simple one, to model the residual deviations from
ODE predictions, or to look for nonlinear covariate relationships in the
latent space.</p>

<table>
  <thead>
    <tr>
      <th>Aspect</th>
      <th>ODE Models</th>
      <th>VAE Models</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Mathematical Form</strong></td>
      <td>Fixed (exponential, polynomial)</td>
      <td>Learned (arbitrary)</td>
    </tr>
    <tr>
      <td><strong>Profile Flexibility</strong></td>
      <td>Limited to assumed structure</td>
      <td>High flexibility</td>
    </tr>
    <tr>
      <td><strong>Interpretability</strong></td>
      <td>High (clear parameters)</td>
      <td>Moderate (latent space)</td>
    </tr>
    <tr>
      <td><strong>Data Requirements</strong></td>
      <td>Moderate</td>
      <td>Higher (for training)</td>
    </tr>
    <tr>
      <td><strong>Extrapolation</strong></td>
      <td>Good (mechanistic)</td>
      <td>Poor outside the training distribution</td>
    </tr>
    <tr>
      <td><strong>Unusual profile shapes</strong></td>
      <td>Needs the right structure chosen</td>
      <td>Learned from data, given enough of it</td>
    </tr>
  </tbody>
</table>

<h2 id="possible-uses">Possible uses</h2>

<p>On the precision dosing side: inferring full profiles from sparse therapeutic
drug monitoring samples, pediatric dosing with complicated age-dependent PK
changes, and organ impairment without assuming the standard clearance
relationships.</p>

<p>On the profile side: biologics with TMDD (saturable elimination without
explicit Michaelis-Menten terms), enterohepatic recirculation (secondary peaks
without adding compartments), and patient-specific absorption patterns learned
from data.</p>

<h2 id="validation">Validation</h2>

<p>For dosing: cross-validate dose prediction accuracy on held-out patients,
compare VAE-predicted doses with clinical outcomes prospectively, and check
that the prediction intervals are calibrated against observed variability.</p>

<p>For profile modeling: visual predictive checks of observed vs VAE-generated
profiles, reconstruction error on independent datasets, and a check that the
learned profiles are consistent with known PK principles.</p>

<h2 id="pros-and-cons">Pros and cons</h2>

<p>In favor: it can fit profiles that are hard to describe with ODEs, it holds
individual variability in the latent space, it handles limited observations
through the learned population structure, and it can pick up nonlinear
covariate relationships.</p>

<p>Against: it needs a lot of training data, the latent space is harder to
interpret than PK parameters, it extrapolates poorly beyond the training
distribution, and it costs more compute than an ODE model.</p>

<h2 id="summary">Summary</h2>

<p>VAEs give a flexible, patient-specific latent representation that can be used
for dose individualization and for profile shapes that standard compartmental
models handle badly. For most problems a well-specified population PK model is
still the thing to beat, and when the time structure matters a neural ODE or a
hybrid model is usually a better fit than a grid-output decoder.</p>]]></content><author><name>Wen Rui Tan</name></author><category term="Machine Learning" /><category term="Pharmacometrics" /><summary type="html"><![CDATA[Application of Variational Autoencoders for precision dosing and modeling pharmacokinetic profiles that cannot be adequately described by traditional ODE-based models]]></summary></entry><entry><title type="html">Indirect Response Models: Mechanistic Framework for Delayed Pharmacodynamic Effects</title><link href="https://wrlog.github.io/2024/02/02/Indirect-Response-Model/" rel="alternate" type="text/html" title="Indirect Response Models: Mechanistic Framework for Delayed Pharmacodynamic Effects" /><published>2024-02-02T00:00:00-05:00</published><updated>2024-02-02T00:00:00-05:00</updated><id>https://wrlog.github.io/2024/02/02/Indirect-Response-Model</id><content type="html" xml:base="https://wrlog.github.io/2024/02/02/Indirect-Response-Model/"><![CDATA[<h2 id="what-indirect-response-models-are">What indirect response models are</h2>

<p>Indirect response (IDR) models are pharmacodynamic models for drugs that don’t
act directly on the thing being measured. The drug changes the turnover
(synthesis or degradation) of some endogenous substance, and the level of that
substance is what we measure as the effect.</p>

<h3 id="direct-vs-indirect-response">Direct vs indirect response</h3>

<p>In a direct response model, concentration maps straight to effect, $E =
f(C(t))$. Anesthetics are the usual example: higher concentration, deeper
anesthesia.</p>

<p>In an indirect response model the chain is longer: concentration changes
turnover, turnover changes the level of a mediator, and the mediator level is
the measured effect. Warfarin doesn’t affect clotting time directly. It
inhibits synthesis of clotting factors, and the factor levels determine
clotting time. Mathematically, the drug acts on $k_{in}$ or $k_{out}$, which
then changes the response $R(t)$.</p>

<h3 id="bathtub-analogy">Bathtub analogy</h3>

<p>Think of the response (a biomarker level, a clotting factor concentration) as
the water level in a bathtub:</p>

<ul>
  <li>$k_{in}$ (or $R_{syn}$) is the faucet, a constant rate of production or
synthesis.</li>
  <li>$k_{out}$ is the drain, a constant rate of elimination or degradation.</li>
  <li>At baseline ($R_0$) the faucet and drain balance and the level is stable.</li>
</ul>

<p>The drug doesn’t change the water level directly. It either turns the faucet
up or down (acts on $k_{in}$) or partly blocks or opens the drain (acts on
$k_{out}$). The level then changes slowly, which is where the delay between
concentration and effect comes from.</p>

<h2 id="the-basic-equation">The basic equation</h2>

<p>The rate of change of the response $R(t)$ is</p>

\[\frac{dR(t)}{dt} = k_{in}(t) - k_{out}(t) \cdot R(t)\]

<p>where:</p>
<ul>
  <li>$R(t)$ is the response variable (e.g., biomarker level, physiological measure)</li>
  <li>$k_{in}(t)$ is the zero-order synthesis rate (may be drug-modulated)</li>
  <li>$k_{out}(t)$ is the first-order elimination rate constant (may be drug-modulated)</li>
</ul>

<p>At baseline (steady state, no drug), $dR/dt = 0$, so</p>

\[R_0 = \frac{k_{in}}{k_{out}}\]

<p>where $R_0$ is the baseline response level.</p>

<h2 id="the-four-basic-idr-models">The four basic IDR models</h2>

<h3 id="model-i-inhibition-of-synthesis-k_in">Model I: inhibition of synthesis ($k_{in}$)</h3>

<p>The drug inhibits production of the endogenous substance:</p>

\[\frac{dR(t)}{dt} = k_{in} \cdot \left(1 - \frac{I_{max} \cdot C(t)}{IC_{50} + C(t)}\right) - k_{out} \cdot R(t)\]

<p>where $I(C) = \frac{I_{max} \cdot C(t)}{IC_{50} + C(t)}$ is the inhibitory
drug effect function, $I_{max}$ is the maximum inhibition (typically 1),
$IC_{50}$ is the concentration giving 50% inhibition, and $C(t)$ is the drug
concentration.</p>

<p>Steady state:</p>

\[R_{ss} = R_0 \cdot \left(1 - \frac{I_{max} \cdot C_{ss}}{IC_{50} + C_{ss}}\right)\]

<p>Example: warfarin inhibits synthesis of the vitamin K-dependent clotting
factors (II, VII, IX, X) in the liver, so the anticoagulant effect, measured
as increased INR, is delayed.</p>

<h3 id="model-ii-stimulation-of-synthesis-k_in">Model II: stimulation of synthesis ($k_{in}$)</h3>

<p>The drug increases production:</p>

\[\frac{dR(t)}{dt} = k_{in} \cdot \left(1 + \frac{S_{max} \cdot C(t)}{SC_{50} + C(t)}\right) - k_{out} \cdot R(t)\]

<p>where $S(C) = \frac{S_{max} \cdot C(t)}{SC_{50} + C(t)}$ is the stimulatory
effect function, $S_{max}$ is the maximum stimulation factor, and $SC_{50}$ is
the concentration giving 50% of maximum stimulation.</p>

<p>Steady state:</p>

\[R_{ss} = R_0 \cdot \left(1 + \frac{S_{max} \cdot C_{ss}}{SC_{50} + C_{ss}}\right)\]

<p>Example: erythropoietin stimulates red blood cell production, and the effect
shows up over days to weeks.</p>

<h3 id="model-iii-inhibition-of-loss-k_out">Model III: inhibition of loss ($k_{out}$)</h3>

<p>The drug reduces the elimination rate:</p>

\[\frac{dR(t)}{dt} = k_{in} - k_{out} \cdot \left(1 - \frac{I_{max} \cdot C(t)}{IC_{50} + C(t)}\right) \cdot R(t)\]

<p>Steady state:</p>

\[R_{ss} = \frac{R_0}{1 - \frac{I_{max} \cdot C_{ss}}{IC_{50} + C_{ss}}}\]

<p>Example: inhibiting loss raises the mediator above baseline, as when a drug
blocks the clearance of an endogenous substance it’s meant to sustain.
Corticosteroids don’t belong here; their classical description is Model I,
suppression of cortisol synthesis.</p>

<h3 id="model-iv-stimulation-of-loss-k_out">Model IV: stimulation of loss ($k_{out}$)</h3>

<p>The drug increases the elimination rate:</p>

\[\frac{dR(t)}{dt} = k_{in} - k_{out} \cdot \left(1 + \frac{S_{max} \cdot C(t)}{SC_{50} + C(t)}\right) \cdot R(t)\]

<p>Steady state:</p>

\[R_{ss} = \frac{R_0}{1 + \frac{S_{max} \cdot C_{ss}}{SC_{50} + C_{ss}}}\]

<p>Example: diuretics increase elimination of fluid and electrolytes, with rapid
onset but sustained effects.</p>

<h2 id="time-course">Time course</h2>

<p>After a step change in drug concentration, the time to reach the new steady
state is set by the effective elimination rate constant. For Model I
(inhibition of $k_{in}$):</p>

\[t_{ss} \approx \frac{4.6}{k_{out}}\]

<p>where $t_{ss}$ is the time to reach 99% of steady state. The response
half-life is $t_{1/2} = 0.693/k_{out}$, so that’s about 6.6 response
half-lives. Five half-lives only gets you to 96.9%. What sets this is the
turnover of the mediator, not the half-life of the drug.</p>

<p>Recovery after stopping the drug is also governed by $k_{out}$:</p>

\[R(t) = R_0 + (R_{drug} - R_0) \cdot e^{-k_{out} \cdot t}\]

<p>Because recovery depends on endogenous turnover and not on the drug’s PK, the
model can predict washout periods and how the dosing interval affects the
response.</p>

<h2 id="hysteresis">Hysteresis</h2>

<p>All four IDR models produce hysteresis, because peak concentration comes
before peak effect. The direction of the loop depends on whether the response
rises or falls, not on which parameter the drug acts on. The lag happens
because drug concentration changes quickly (set by PK) while the response
changes slowly (set by $k_{out}$).</p>

<p>The width of the loop reflects the delay. The enclosed area is the line
integral around the closed curve in the concentration-response plane,</p>

\[A = \tfrac{1}{2} \oint \left( C \, dR - R \, dC \right)\]

<p>but in practice people read the loop qualitatively instead of integrating it.
Its direction tells you whether effect lags concentration. An IDR model
models the turnover that causes the loop, so you don’t need to quantify the
loop itself, and in many cases you don’t need an effect compartment or transit
compartments either.</p>

<h2 id="nonmem-implementation">NONMEM implementation</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$PK
CL   = THETA(1) * EXP(ETA(1))
V    = THETA(2) * EXP(ETA(2))
KOUT = THETA(3) * EXP(ETA(3))
R0   = THETA(4) * EXP(ETA(4))
IMAX = THETA(5)
IC50 = THETA(6)
KIN  = R0 * KOUT        ; baseline constraint, so R0 is estimated directly
S1   = V
A_0(2) = R0         ; start the response at baseline

$DES
; Concentration must be formed here, not in $PK, because it changes at
; every integration step.
CONC = A(1)/V
DADT(1) = -(CL/V)*A(1)
DADT(2) = KIN*(1 - (IMAX*CONC)/(IC50 + CONC)) - KOUT*A(2)

$ERROR
IPRED = A(2)
Y = IPRED*(1 + ERR(1)) + ERR(2)
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">A(1)</code> is the drug amount, <code class="language-plaintext highlighter-rouge">A(2)</code> is the response, <code class="language-plaintext highlighter-rouge">KIN</code> and <code class="language-plaintext highlighter-rouge">KOUT</code> are the
turnover parameters, and <code class="language-plaintext highlighter-rouge">IMAX</code> and <code class="language-plaintext highlighter-rouge">IC50</code> describe the drug’s inhibition.</p>

<p>Two details matter. The response compartment has to be initialized at
baseline (<code class="language-plaintext highlighter-rouge">A_0(2) = R0</code> in <code class="language-plaintext highlighter-rouge">$PK</code>); otherwise the model starts from zero and spends the
first part of the profile climbing up to baseline. And writing $k_{in}$ as
$R_0 \cdot k_{out}$ means the baseline is estimated directly, which is better
identified and easier to interpret than estimating $k_{in}$ by itself.</p>

<h2 id="why-use-them-over-direct-response-models">Why use them over direct response models</h2>

<p>The model describes an actual biological process (turnover) instead of just
fitting a curve. $k_{in}$ and $k_{out}$ have physiological meaning, which
helps when extrapolating to other dosing regimens and patient populations. The
delay is accounted for without extra compartments. And onset delays, recovery
times and steady-state relationships can be read straight from the parameters.</p>

<h2 id="choosing-between-the-four">Choosing between the four</h2>

<p>Picking one of the four models comes down to knowledge of the drug’s
mechanism, looking at concentration-effect plots, statistical comparison by
objective function value (OFV) or information criteria (AIC, BIC), and
goodness of fit, including visual predictive checks (VPC) and
prediction-corrected VPC.</p>

<p>Useful diagnostic plots:</p>
<ul>
  <li>Concentration-time profiles</li>
  <li>Response-time profiles</li>
  <li>Concentration-response plots (hysteresis loops)</li>
  <li>Individual fits and residuals</li>
</ul>

<h2 id="where-theyve-been-used">Where they’ve been used</h2>

<ul>
  <li>Anticoagulants (warfarin, direct thrombin inhibitors)</li>
  <li>Hormone therapies (erythropoietin, growth hormone)</li>
  <li>Immunosuppressants (corticosteroids, calcineurin inhibitors)</li>
  <li>Cardiovascular drugs (ACE inhibitors, beta-blockers)</li>
  <li>Oncology (biomarker responses to targeted therapies)</li>
</ul>

<h2 id="summary">Summary</h2>

<p>IDR models describe delayed PD effects through the turnover of an endogenous
mediator. Because the structure follows the biology, they tend to predict
better than empirical direct response models, and they’re the standard choice
when there’s a lag between exposure and response, especially where biomarker
dynamics drive clinical decisions.</p>]]></content><author><name>Wen Rui Tan</name></author><category term="Pharmacodynamics" /><summary type="html"><![CDATA[Notes on indirect response (IDR) models: the four basic models, their equations, NONMEM code and where they are used]]></summary></entry><entry><title type="html">D-Optimal Design Optimization for Pediatric Clinical Trials</title><link href="https://wrlog.github.io/2024/01/10/D-Optimal-Design-with-PopED/" rel="alternate" type="text/html" title="D-Optimal Design Optimization for Pediatric Clinical Trials" /><published>2024-01-10T00:00:00-05:00</published><updated>2024-01-10T00:00:00-05:00</updated><id>https://wrlog.github.io/2024/01/10/D-Optimal-Design-with-PopED</id><content type="html" xml:base="https://wrlog.github.io/2024/01/10/D-Optimal-Design-with-PopED/"><![CDATA[<h2 id="why-optimise-the-design">Why optimise the design</h2>

<p>In paediatrics the limit is usually blood volume rather than the model. A
neonate can’t give twelve samples, and parents won’t consent to a study that
asks for them. So the question becomes: if we can take four samples, which
four?</p>

<p>That’s an optimisation problem with a clear objective. The Fisher information
matrix describes how much the data will tell you about the parameters. A
D-optimal design maximises its determinant, which is the same as minimising
the volume of the joint confidence region around the parameter estimates.
PopED evaluates that criterion for a population model and searches over
sampling times, doses and group sizes.</p>

<p>One caveat first: the optimal design depends on the model and the parameter
values you assume. If the prior estimates are badly off, the design is optimal
for the wrong model, so how well a design holds up under parameter
misspecification matters as much as its nominal efficiency.</p>

<h2 id="model">Model</h2>

<p>One-compartment oral model with first-order absorption, dosed to steady state.
Clearance and volume have between-subject variability and absorption doesn’t,
which is a common simplification when absorption is poorly identified from
sparse data.</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">library</span><span class="p">(</span><span class="n">PopED</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">ggplot2</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">dplyr</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">viridis</span><span class="p">)</span><span class="w">

</span><span class="n">sfg</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">bpop</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">,</span><span class="w"> </span><span class="n">bocc</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">parameters</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="w">
    </span><span class="n">CL</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">bpop</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nf">exp</span><span class="p">(</span><span class="n">b</span><span class="p">[</span><span class="m">1</span><span class="p">]),</span><span class="w">
    </span><span class="n">V</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">bpop</span><span class="p">[</span><span class="m">2</span><span class="p">]</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nf">exp</span><span class="p">(</span><span class="n">b</span><span class="p">[</span><span class="m">2</span><span class="p">]),</span><span class="w">
    </span><span class="n">KA</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">bpop</span><span class="p">[</span><span class="m">3</span><span class="p">],</span><span class="w">
    </span><span class="n">DOSE</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">a</span><span class="p">[</span><span class="m">1</span><span class="p">],</span><span class="w">
    </span><span class="n">TAU</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">a</span><span class="p">[</span><span class="m">2</span><span class="p">]</span><span class="w">
  </span><span class="p">)</span><span class="w">
  </span><span class="nf">return</span><span class="p">(</span><span class="n">parameters</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="c1"># One-compartment oral, multiple dose. The two bracketed ratios are the</span><span class="w">
</span><span class="c1"># accumulation terms. Without them this is the single-dose solution, and</span><span class="w">
</span><span class="c1"># using TAU and N without them quietly models every dose in isolation.</span><span class="w">
</span><span class="n">ff</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">model_switch</span><span class="p">,</span><span class="w"> </span><span class="n">xt</span><span class="p">,</span><span class="w"> </span><span class="n">parameters</span><span class="p">,</span><span class="w"> </span><span class="n">poped.db</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">with</span><span class="p">(</span><span class="n">as.list</span><span class="p">(</span><span class="n">parameters</span><span class="p">),</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">N</span><span class="w">  </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">floor</span><span class="p">(</span><span class="n">xt</span><span class="o">/</span><span class="n">TAU</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="m">1</span><span class="w">
    </span><span class="n">KE</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">CL</span><span class="o">/</span><span class="n">V</span><span class="w">
    </span><span class="n">y</span><span class="w">  </span><span class="o">&lt;-</span><span class="w"> </span><span class="p">(</span><span class="n">DOSE</span><span class="o">/</span><span class="n">V</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="p">(</span><span class="n">KA</span><span class="o">/</span><span class="p">(</span><span class="n">KA</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">KE</span><span class="p">))</span><span class="w"> </span><span class="o">*</span><span class="w">
      </span><span class="p">(</span><span class="nf">exp</span><span class="p">(</span><span class="o">-</span><span class="n">KE</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="p">(</span><span class="n">xt</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="p">(</span><span class="n">N</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">TAU</span><span class="p">))</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="p">(</span><span class="m">1</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="nf">exp</span><span class="p">(</span><span class="o">-</span><span class="n">N</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">TAU</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">KE</span><span class="p">))</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="p">(</span><span class="m">1</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="nf">exp</span><span class="p">(</span><span class="o">-</span><span class="n">TAU</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">KE</span><span class="p">))</span><span class="w"> </span><span class="o">-</span><span class="w">
       </span><span class="nf">exp</span><span class="p">(</span><span class="o">-</span><span class="n">KA</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="p">(</span><span class="n">xt</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="p">(</span><span class="n">N</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">TAU</span><span class="p">))</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="p">(</span><span class="m">1</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="nf">exp</span><span class="p">(</span><span class="o">-</span><span class="n">N</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">TAU</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">KA</span><span class="p">))</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="p">(</span><span class="m">1</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="nf">exp</span><span class="p">(</span><span class="o">-</span><span class="n">TAU</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">KA</span><span class="p">)))</span><span class="w">
    </span><span class="nf">return</span><span class="p">(</span><span class="nf">list</span><span class="p">(</span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">y</span><span class="p">,</span><span class="w"> </span><span class="n">poped.db</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">poped.db</span><span class="p">))</span><span class="w">
  </span><span class="p">})</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="n">feps</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">model_switch</span><span class="p">,</span><span class="w"> </span><span class="n">xt</span><span class="p">,</span><span class="w"> </span><span class="n">parameters</span><span class="p">,</span><span class="w"> </span><span class="n">epsi</span><span class="p">,</span><span class="w"> </span><span class="n">poped.db</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">returnArgs</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ff</span><span class="p">(</span><span class="n">model_switch</span><span class="p">,</span><span class="w"> </span><span class="n">xt</span><span class="p">,</span><span class="w"> </span><span class="n">parameters</span><span class="p">,</span><span class="w"> </span><span class="n">poped.db</span><span class="p">)</span><span class="w">
  </span><span class="n">y</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">returnArgs</span><span class="p">[[</span><span class="m">1</span><span class="p">]]</span><span class="w">
  </span><span class="n">poped.db</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">returnArgs</span><span class="p">[[</span><span class="m">2</span><span class="p">]]</span><span class="w">

  </span><span class="n">y</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="p">(</span><span class="m">1</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">epsi</span><span class="p">[,</span><span class="w"> </span><span class="m">1</span><span class="p">])</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">epsi</span><span class="p">[,</span><span class="w"> </span><span class="m">2</span><span class="p">]</span><span class="w">
  </span><span class="nf">return</span><span class="p">(</span><span class="nf">list</span><span class="p">(</span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">y</span><span class="p">,</span><span class="w"> </span><span class="n">poped.db</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">poped.db</span><span class="p">))</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>The residual error is combined proportional and additive. This matters: with a
purely proportional error the optimiser will put samples in the terminal tail
where concentrations are near zero, because the <em>relative</em> error there is the
same. The additive part stops that.</p>

<h2 id="design-space">Design space</h2>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">poped.db</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">create.poped.database</span><span class="p">(</span><span class="w">
  </span><span class="n">ff_fun</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ff</span><span class="p">,</span><span class="w">
  </span><span class="n">fg_fun</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">sfg</span><span class="p">,</span><span class="w">
  </span><span class="n">fError_fun</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">feps</span><span class="p">,</span><span class="w">
  </span><span class="n">bpop</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="n">CL</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">2.5</span><span class="p">,</span><span class="w"> </span><span class="n">V</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">15</span><span class="p">,</span><span class="w"> </span><span class="n">KA</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1.5</span><span class="p">),</span><span class="w">
  </span><span class="n">d</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="n">CL</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.3</span><span class="p">,</span><span class="w"> </span><span class="n">V</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.25</span><span class="p">),</span><span class="w">
  </span><span class="n">sigma</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="n">prop</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.2</span><span class="p">,</span><span class="w"> </span><span class="n">add</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.1</span><span class="p">),</span><span class="w">
  </span><span class="n">notfixed_sigma</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="p">),</span><span class="w">
  </span><span class="n">m</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w">
  </span><span class="n">groupsize</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">20</span><span class="p">,</span><span class="w"> </span><span class="m">20</span><span class="p">),</span><span class="w">
  </span><span class="n">xt</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">0.5</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="m">6</span><span class="p">,</span><span class="w"> </span><span class="m">12</span><span class="p">,</span><span class="w"> </span><span class="m">24</span><span class="p">,</span><span class="w"> </span><span class="m">48</span><span class="p">),</span><span class="w">
  </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="n">DOSE</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">100</span><span class="p">,</span><span class="w"> </span><span class="n">TAU</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">6</span><span class="p">),</span><span class="w">
  </span><span class="n">minxt</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0</span><span class="p">,</span><span class="w">
  </span><span class="n">maxxt</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">72</span><span class="p">,</span><span class="w">
  </span><span class="n">discrete_xt</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="n">seq</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="m">72</span><span class="p">,</span><span class="w"> </span><span class="n">by</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.5</span><span class="p">)),</span><span class="w">
  </span><span class="n">discrete_a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="nf">c</span><span class="p">(</span><span class="m">50</span><span class="p">,</span><span class="w"> </span><span class="m">75</span><span class="p">,</span><span class="w"> </span><span class="m">100</span><span class="p">,</span><span class="w"> </span><span class="m">125</span><span class="p">,</span><span class="w"> </span><span class="m">150</span><span class="p">)),</span><span class="w">
  </span><span class="n">maxa</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="n">DOSE</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">150</span><span class="p">,</span><span class="w"> </span><span class="n">TAU</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">12</span><span class="p">),</span><span class="w">
  </span><span class="n">mina</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="n">DOSE</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">50</span><span class="p">,</span><span class="w"> </span><span class="n">TAU</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">4</span><span class="p">),</span><span class="w">
  </span><span class="n">bUseGrouped_xt</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1</span><span class="p">,</span><span class="w">
  </span><span class="c1"># Both groups share one sampling schedule: the six indices repeat across</span><span class="w">
  </span><span class="c1"># groups. Numbering them 1,1,1,1,1,1,2,2,2,2,2,2 instead would constrain all</span><span class="w">
  </span><span class="c1"># six times *within* a group to be equal, collapsing the design to a single</span><span class="w">
  </span><span class="c1"># timepoint per group.</span><span class="w">
  </span><span class="n">G_xt</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">matrix</span><span class="p">(</span><span class="nf">rep</span><span class="p">(</span><span class="m">1</span><span class="o">:</span><span class="m">6</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="p">),</span><span class="w"> </span><span class="n">nrow</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">byrow</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">),</span><span class="w">
  </span><span class="n">ourzero</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0</span><span class="w">
</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">discrete_xt</code> restricts sampling to the half hour. On a continuous scale the
optimiser returns times like 3.47 h, which no ward is going to hit, and a
design that only works if it’s executed perfectly isn’t much use in practice.</p>

<h2 id="running-the-optimisation">Running the optimisation</h2>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">output</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">poped_optim</span><span class="p">(</span><span class="n">poped.db</span><span class="p">,</span><span class="w"> </span><span class="n">opt_xt</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">,</span><span class="w"> </span><span class="n">opt_a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">FALSE</span><span class="p">,</span><span class="w">
                      </span><span class="n">method</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="s2">"ARS"</span><span class="p">,</span><span class="w"> </span><span class="s2">"BFGS"</span><span class="p">),</span><span class="w">
                      </span><span class="n">control</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="n">iter_max</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">50</span><span class="p">))</span><span class="w">

</span><span class="n">plot_model_prediction</span><span class="p">(</span><span class="n">poped.db</span><span class="p">,</span><span class="w"> </span><span class="n">model_num_points</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">500</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">ARS</code> (adaptive random search) explores globally and <code class="language-plaintext highlighter-rouge">BFGS</code> refines locally.
Running them in sequence is the usual pattern because the D-criterion surface
has local optima, and a gradient method on its own stays in whichever one it
started closest to. Doses are fixed here (<code class="language-plaintext highlighter-rouge">opt_a = FALSE</code>) because the clinical
protocol set the dose.</p>

<h2 id="comparing-candidate-designs">Comparing candidate designs</h2>

<p>The D-criterion itself is on a scale nobody can interpret. For a protocol team
the useful number is efficiency relative to a reference design, i.e. how much
information a cheaper design keeps.</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">compare_designs</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">poped.db</span><span class="p">,</span><span class="w"> </span><span class="n">n_samples</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">3</span><span class="p">,</span><span class="w"> </span><span class="m">4</span><span class="p">,</span><span class="w"> </span><span class="m">5</span><span class="p">,</span><span class="w"> </span><span class="m">6</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">results</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">data.frame</span><span class="p">()</span><span class="w">

  </span><span class="n">candidate_times</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="w">
    </span><span class="s2">"Sparse"</span><span class="w">   </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">0.5</span><span class="p">,</span><span class="w"> </span><span class="m">6</span><span class="p">,</span><span class="w"> </span><span class="m">24</span><span class="p">),</span><span class="w">
    </span><span class="s2">"Moderate"</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">0.5</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="m">6</span><span class="p">,</span><span class="w"> </span><span class="m">12</span><span class="p">,</span><span class="w"> </span><span class="m">24</span><span class="p">),</span><span class="w">
    </span><span class="s2">"Dense"</span><span class="w">    </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">0.5</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="m">4</span><span class="p">,</span><span class="w"> </span><span class="m">6</span><span class="p">,</span><span class="w"> </span><span class="m">12</span><span class="p">,</span><span class="w"> </span><span class="m">18</span><span class="p">,</span><span class="w"> </span><span class="m">24</span><span class="p">),</span><span class="w">
    </span><span class="s2">"Optimal"</span><span class="w">  </span><span class="o">=</span><span class="w"> </span><span class="nf">as.numeric</span><span class="p">(</span><span class="n">output</span><span class="o">$</span><span class="n">xt</span><span class="p">[</span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="p">])</span><span class="w">
  </span><span class="p">)</span><span class="w">

  </span><span class="n">reference_ofv</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">PopED</span><span class="o">::</span><span class="n">evaluate_design</span><span class="p">(</span><span class="n">poped.db</span><span class="p">)</span><span class="o">$</span><span class="n">ofv</span><span class="w">

  </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">design_name</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="nf">names</span><span class="p">(</span><span class="n">candidate_times</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">times</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">candidate_times</span><span class="p">[[</span><span class="n">design_name</span><span class="p">]]</span><span class="w">
    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="nf">length</span><span class="p">(</span><span class="n">times</span><span class="p">)</span><span class="w"> </span><span class="o">%in%</span><span class="w"> </span><span class="n">n_samples</span><span class="p">)</span><span class="w"> </span><span class="k">next</span><span class="w">

    </span><span class="n">poped.db.tmp</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">poped.db</span><span class="w">
    </span><span class="n">poped.db.tmp</span><span class="o">$</span><span class="n">xt</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">matrix</span><span class="p">(</span><span class="nf">rep</span><span class="p">(</span><span class="n">times</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="p">),</span><span class="w"> </span><span class="n">nrow</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">byrow</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">)</span><span class="w">

    </span><span class="c1"># PopED::evaluate_design, not this function -- naming the wrapper</span><span class="w">
    </span><span class="c1"># evaluate_design() shadows it and makes the call infinitely recursive.</span><span class="w">
    </span><span class="n">ofv</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">PopED</span><span class="o">::</span><span class="n">evaluate_design</span><span class="p">(</span><span class="n">poped.db.tmp</span><span class="p">)</span><span class="o">$</span><span class="n">ofv</span><span class="w">

    </span><span class="n">results</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">rbind</span><span class="p">(</span><span class="n">results</span><span class="p">,</span><span class="w"> </span><span class="n">data.frame</span><span class="p">(</span><span class="w">
      </span><span class="n">N_Samples</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">times</span><span class="p">),</span><span class="w">
      </span><span class="n">Design</span><span class="w">    </span><span class="o">=</span><span class="w"> </span><span class="n">design_name</span><span class="p">,</span><span class="w">
      </span><span class="n">OFV</span><span class="w">       </span><span class="o">=</span><span class="w"> </span><span class="n">ofv</span><span class="p">,</span><span class="w">
      </span><span class="c1"># D-efficiency relative to the reference, normalised by the number of</span><span class="w">
      </span><span class="c1"># parameters. Comparing raw determinants across designs is meaningless.</span><span class="w">
      </span><span class="n">Efficiency</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">100</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="p">(</span><span class="n">ofv</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">reference_ofv</span><span class="p">)</span><span class="o">^</span><span class="p">(</span><span class="m">1</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">poped.db</span><span class="o">$</span><span class="n">parameters</span><span class="o">$</span><span class="n">bpop</span><span class="p">))</span><span class="w">
    </span><span class="p">))</span><span class="w">
  </span><span class="p">}</span><span class="w">

  </span><span class="n">results</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="n">design_comparison</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">compare_designs</span><span class="p">(</span><span class="n">poped.db</span><span class="p">)</span><span class="w">

</span><span class="n">p1</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">design_comparison</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
  </span><span class="n">ggplot</span><span class="p">(</span><span class="n">aes</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">factor</span><span class="p">(</span><span class="n">N_Samples</span><span class="p">),</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Efficiency</span><span class="p">,</span><span class="w"> </span><span class="n">fill</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Design</span><span class="p">))</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">geom_bar</span><span class="p">(</span><span class="n">stat</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"identity"</span><span class="p">,</span><span class="w"> </span><span class="n">position</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"dodge"</span><span class="p">,</span><span class="w"> </span><span class="n">alpha</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.8</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">geom_hline</span><span class="p">(</span><span class="n">yintercept</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">100</span><span class="p">,</span><span class="w"> </span><span class="n">linetype</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"dashed"</span><span class="p">,</span><span class="w"> </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"red"</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">annotate</span><span class="p">(</span><span class="s2">"text"</span><span class="p">,</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">105</span><span class="p">,</span><span class="w"> </span><span class="n">label</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Reference design"</span><span class="p">,</span><span class="w">
           </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"red"</span><span class="p">,</span><span class="w"> </span><span class="n">size</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">3</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">scale_fill_viridis_d</span><span class="p">(</span><span class="n">begin</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.2</span><span class="p">,</span><span class="w"> </span><span class="n">end</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.8</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">labs</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Number of Samples"</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"D-efficiency (%)"</span><span class="p">,</span><span class="w">
       </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Design efficiency by number of samples"</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">theme_minimal</span><span class="p">()</span><span class="w">

</span><span class="n">print</span><span class="p">(</span><span class="n">p1</span><span class="p">)</span><span class="w">

</span><span class="n">cat</span><span class="p">(</span><span class="s2">"\n=== Optimised sampling schedule ===\n"</span><span class="p">)</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="n">sprintf</span><span class="p">(</span><span class="s2">"Times (h): %s\n"</span><span class="p">,</span><span class="w"> </span><span class="n">paste</span><span class="p">(</span><span class="nf">round</span><span class="p">(</span><span class="nf">as.numeric</span><span class="p">(</span><span class="n">output</span><span class="o">$</span><span class="n">xt</span><span class="p">[</span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="p">]),</span><span class="w"> </span><span class="m">1</span><span class="p">),</span><span class="w"> </span><span class="n">collapse</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">", "</span><span class="p">)))</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="n">sprintf</span><span class="p">(</span><span class="s2">"Samples per patient: %d\n"</span><span class="p">,</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">output</span><span class="o">$</span><span class="n">xt</span><span class="p">[</span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="p">])))</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="s2">"\nEfficiency of candidate designs, relative to the reference:\n"</span><span class="p">)</span><span class="w">
</span><span class="n">print</span><span class="p">(</span><span class="n">design_comparison</span><span class="p">[,</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="s2">"Design"</span><span class="p">,</span><span class="w"> </span><span class="s2">"N_Samples"</span><span class="p">,</span><span class="w"> </span><span class="s2">"Efficiency"</span><span class="p">)],</span><span class="w"> </span><span class="n">row.names</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">FALSE</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<h2 id="reading-the-result">Reading the result</h2>

<p>I look at the shape of the efficiency curve more than any single number. It’s
usually steep at the low end and flat at the top: going from three samples to
five gains a lot, and going from six to eight gains very little. The flat part
is what to show a protocol team, since those extra draws cost the patient
something and add almost no precision.</p>

<p>Two caveats to mention whenever these results are presented:</p>

<ul>
  <li>The design is conditional on the assumed parameters. Before committing,
re-evaluate the chosen design across a plausible range of CL and V. If its
efficiency drops off sharply, the design is fragile, and ED-optimality (which
averages over a prior on the parameters) is a better criterion.</li>
  <li>D-optimality optimises parameter precision. If the decision depends on a
predicted exposure or a probability of target attainment, the criterion
should be the precision of that quantity, which gives a different design.</li>
</ul>

<p>Not covered here: cost functions over sampling windows, ED-optimal designs
under parameter uncertainty, and designs optimised for model discrimination
instead of parameter precision. PopED supports all three.</p>]]></content><author><name>Wen Rui Tan</name></author><category term="Pharmacometrics" /><summary type="html"><![CDATA[Using PopED to determine optimal sampling timepoints that maximize information gain while minimizing patient burden]]></summary></entry><entry><title type="html">Monte Carlo Simulation for Model-Informed Dose Optimization</title><link href="https://wrlog.github.io/2023/09/22/Monte-Carlo-Simulation-for-Dose-Optimization/" rel="alternate" type="text/html" title="Monte Carlo Simulation for Model-Informed Dose Optimization" /><published>2023-09-22T00:00:00-04:00</published><updated>2023-09-22T00:00:00-04:00</updated><id>https://wrlog.github.io/2023/09/22/Monte-Carlo-Simulation-for-Dose-Optimization</id><content type="html" xml:base="https://wrlog.github.io/2023/09/22/Monte-Carlo-Simulation-for-Dose-Optimization/"><![CDATA[<h2 id="why-simulate">Why simulate</h2>

<p>A population PK model gives you a typical patient and a description of how
much individuals vary around them. But doses are chosen for a population, so
what you want to know is what fraction of that population reaches the target
on a given regimen.</p>

<p>Monte Carlo simulation answers that. The steps are always roughly:</p>

<ol>
  <li>Draw a virtual population with covariates covering the range you actually
treat.</li>
  <li>Draw individual PK parameters for each subject from the model’s
between-subject variability.</li>
  <li>Simulate every subject through each candidate regimen.</li>
  <li>Summarise across the population as probability of target attainment (PTA),
rather than looking at the profile of the average patient.</li>
</ol>

<p>Step 4 is the one people skip. Checking that the typical patient’s trough
clears the target is a much easier question, and it will recommend a dose that
fails in about half the population.</p>

<h2 id="setting-up">Setting up</h2>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">library</span><span class="p">(</span><span class="n">tidyverse</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">mrgsolve</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">viridis</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">patchwork</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">pracma</span><span class="p">)</span><span class="w">   </span><span class="c1"># trapz(), used for AUC below</span><span class="w">

</span><span class="c1"># mrgsolve's built-in library has no piperacillin model. Point mread() at a</span><span class="w">
</span><span class="c1"># model file of your own, or start from one of the shipped structural models</span><span class="w">
</span><span class="c1"># (modlib() provides pk1, pk2, irm1-irm4, emax and others).</span><span class="w">
</span><span class="n">mod</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">mread</span><span class="p">(</span><span class="s2">"pk2"</span><span class="p">,</span><span class="w"> </span><span class="n">modlib</span><span class="p">())</span><span class="w">

</span><span class="n">generate_virtual_population</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">n</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1000</span><span class="p">,</span><span class="w"> </span><span class="n">age_range</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">0.5</span><span class="p">,</span><span class="w"> </span><span class="m">18</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">age</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">runif</span><span class="p">(</span><span class="n">n</span><span class="p">,</span><span class="w"> </span><span class="n">age_range</span><span class="p">[</span><span class="m">1</span><span class="p">],</span><span class="w"> </span><span class="n">age_range</span><span class="p">[</span><span class="m">2</span><span class="p">])</span><span class="w">
  </span><span class="n">weight</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">exp</span><span class="p">(</span><span class="n">rnorm</span><span class="p">(</span><span class="n">n</span><span class="p">,</span><span class="w"> </span><span class="nf">log</span><span class="p">(</span><span class="m">3.5</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">age</span><span class="o">^</span><span class="m">0.75</span><span class="p">),</span><span class="w"> </span><span class="m">0.3</span><span class="p">))</span><span class="w">
  </span><span class="n">bsa</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">0.024265</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="p">(</span><span class="n">weight</span><span class="o">^</span><span class="m">0.5378</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="p">((</span><span class="n">age</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="m">12</span><span class="p">)</span><span class="o">^</span><span class="m">0.3964</span><span class="p">)</span><span class="w">
  </span><span class="n">crcl</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">0.48</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">weight</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="p">(</span><span class="m">140</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">age</span><span class="p">)</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="p">(</span><span class="m">0.7</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="m">1.0</span><span class="p">)</span><span class="w">
  </span><span class="n">crcl</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">pmax</span><span class="p">(</span><span class="m">10</span><span class="p">,</span><span class="w"> </span><span class="n">pmin</span><span class="p">(</span><span class="m">200</span><span class="p">,</span><span class="w"> </span><span class="n">crcl</span><span class="p">))</span><span class="w">
  
  </span><span class="n">data.frame</span><span class="p">(</span><span class="w">
    </span><span class="n">ID</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">n</span><span class="p">,</span><span class="w">
    </span><span class="n">AGE</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">age</span><span class="p">,</span><span class="w">
    </span><span class="n">WT</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">weight</span><span class="p">,</span><span class="w">
    </span><span class="n">BSA</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">bsa</span><span class="p">,</span><span class="w">
    </span><span class="n">CRCL</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">crcl</span><span class="p">,</span><span class="w">
    </span><span class="n">SEX</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">sample</span><span class="p">(</span><span class="nf">c</span><span class="p">(</span><span class="s2">"M"</span><span class="p">,</span><span class="w"> </span><span class="s2">"F"</span><span class="p">),</span><span class="w"> </span><span class="n">n</span><span class="p">,</span><span class="w"> </span><span class="n">replace</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">)</span><span class="w">
  </span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="n">run_mc_simulation</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">model</span><span class="p">,</span><span class="w"> </span><span class="n">population</span><span class="p">,</span><span class="w"> </span><span class="n">dose_mgkg</span><span class="p">,</span><span class="w"> </span><span class="n">interval_hours</span><span class="p">,</span><span class="w"> </span><span class="n">n_doses</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">4</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">results</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">list</span><span class="p">()</span><span class="w">
  
  </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">nrow</span><span class="p">(</span><span class="n">population</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">patient</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">population</span><span class="p">[</span><span class="n">i</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w">
    
    </span><span class="n">CL</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">2.5</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="p">(</span><span class="n">patient</span><span class="o">$</span><span class="n">CRCL</span><span class="o">/</span><span class="m">80</span><span class="p">)</span><span class="o">^</span><span class="m">0.75</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="p">(</span><span class="n">patient</span><span class="o">$</span><span class="n">WT</span><span class="o">/</span><span class="m">20</span><span class="p">)</span><span class="o">^</span><span class="m">0.75</span><span class="w"> </span><span class="o">*</span><span class="w"> 
          </span><span class="nf">exp</span><span class="p">(</span><span class="n">rnorm</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="m">0.3</span><span class="p">))</span><span class="w">
    </span><span class="n">V</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">15</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="p">(</span><span class="n">patient</span><span class="o">$</span><span class="n">WT</span><span class="o">/</span><span class="m">20</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nf">exp</span><span class="p">(</span><span class="n">rnorm</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="m">0.25</span><span class="p">))</span><span class="w">
    
    </span><span class="n">ev</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ev</span><span class="p">(</span><span class="n">amt</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">dose_mgkg</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">patient</span><span class="o">$</span><span class="n">WT</span><span class="p">,</span><span class="w"> 
             </span><span class="n">ii</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">interval_hours</span><span class="p">,</span><span class="w"> 
             </span><span class="n">addl</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">n_doses</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w">
    
    </span><span class="n">out</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">model</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">param</span><span class="p">(</span><span class="n">CL</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">CL</span><span class="p">,</span><span class="w"> </span><span class="n">V</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">V</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">ev</span><span class="p">(</span><span class="n">ev</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">mrgsim</span><span class="p">(</span><span class="n">end</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">interval_hours</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">n_doses</span><span class="p">,</span><span class="w"> </span><span class="n">delta</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.5</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">as.data.frame</span><span class="p">()</span><span class="w">
    
    </span><span class="n">out</span><span class="o">$</span><span class="n">ID</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">patient</span><span class="o">$</span><span class="n">ID</span><span class="w">
    </span><span class="n">out</span><span class="o">$</span><span class="n">WT</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">patient</span><span class="o">$</span><span class="n">WT</span><span class="w">
    </span><span class="n">out</span><span class="o">$</span><span class="n">CRCL</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">patient</span><span class="o">$</span><span class="n">CRCL</span><span class="w">
    </span><span class="n">out</span><span class="o">$</span><span class="n">AGE</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">patient</span><span class="o">$</span><span class="n">AGE</span><span class="w">
    
    </span><span class="n">results</span><span class="p">[[</span><span class="n">i</span><span class="p">]]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">out</span><span class="w">
  </span><span class="p">}</span><span class="w">
  
  </span><span class="n">bind_rows</span><span class="p">(</span><span class="n">results</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="n">calculate_pta</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">sim_data</span><span class="p">,</span><span class="w"> </span><span class="n">target_threshold</span><span class="p">,</span><span class="w"> </span><span class="n">time_window</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">NULL</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="nf">is.null</span><span class="p">(</span><span class="n">time_window</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">sim_data</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sim_data</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">filter</span><span class="p">(</span><span class="n">time</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="n">time_window</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">time</span><span class="w"> </span><span class="o">&lt;=</span><span class="w"> </span><span class="n">time_window</span><span class="p">[</span><span class="m">2</span><span class="p">])</span><span class="w">
  </span><span class="p">}</span><span class="w">
  
  </span><span class="n">sim_data</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
    </span><span class="n">group_by</span><span class="p">(</span><span class="n">ID</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
    </span><span class="n">summarise</span><span class="p">(</span><span class="w">
      </span><span class="n">Cmin</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">min</span><span class="p">(</span><span class="n">CP</span><span class="p">,</span><span class="w"> </span><span class="n">na.rm</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">),</span><span class="w">
      </span><span class="n">Cmax</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">max</span><span class="p">(</span><span class="n">CP</span><span class="p">,</span><span class="w"> </span><span class="n">na.rm</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">),</span><span class="w">
      </span><span class="n">AUC</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">pracma</span><span class="o">::</span><span class="n">trapz</span><span class="p">(</span><span class="n">time</span><span class="p">,</span><span class="w"> </span><span class="n">CP</span><span class="p">),</span><span class="w">
      </span><span class="n">T_above_target</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">sum</span><span class="p">(</span><span class="n">CP</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="n">target_threshold</span><span class="p">,</span><span class="w"> </span><span class="n">na.rm</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="m">0.5</span><span class="p">,</span><span class="w">
      </span><span class="n">PTA</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ifelse</span><span class="p">(</span><span class="n">Cmin</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="n">target_threshold</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="m">0</span><span class="p">),</span><span class="w">
      </span><span class="n">WT</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">first</span><span class="p">(</span><span class="n">WT</span><span class="p">),</span><span class="w">
      </span><span class="n">CRCL</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">first</span><span class="p">(</span><span class="n">CRCL</span><span class="p">),</span><span class="w">
      </span><span class="n">AGE</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">first</span><span class="p">(</span><span class="n">AGE</span><span class="p">)</span><span class="w">
    </span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="n">evaluate_dosing_regimens</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">model</span><span class="p">,</span><span class="w"> </span><span class="n">population</span><span class="p">,</span><span class="w"> </span><span class="n">regimens</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">results_list</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">list</span><span class="p">()</span><span class="w">
  
  </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">regimen_name</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="nf">names</span><span class="p">(</span><span class="n">regimens</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">regimen</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">regimens</span><span class="p">[[</span><span class="n">regimen_name</span><span class="p">]]</span><span class="w">
    
    </span><span class="n">sim_data</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">run_mc_simulation</span><span class="p">(</span><span class="w">
      </span><span class="n">model</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">model</span><span class="p">,</span><span class="w">
      </span><span class="n">population</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">population</span><span class="p">,</span><span class="w">
      </span><span class="n">dose_mgkg</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">regimen</span><span class="o">$</span><span class="n">dose</span><span class="p">,</span><span class="w">
      </span><span class="n">interval_hours</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">regimen</span><span class="o">$</span><span class="n">interval</span><span class="p">,</span><span class="w">
      </span><span class="n">n_doses</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">regimen</span><span class="o">$</span><span class="n">n_doses</span><span class="w">
    </span><span class="p">)</span><span class="w">
    
    </span><span class="n">pta_64</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">calculate_pta</span><span class="p">(</span><span class="n">sim_data</span><span class="p">,</span><span class="w"> </span><span class="n">target_threshold</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">64</span><span class="p">)</span><span class="w">
    </span><span class="n">pta_32</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">calculate_pta</span><span class="p">(</span><span class="n">sim_data</span><span class="p">,</span><span class="w"> </span><span class="n">target_threshold</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">32</span><span class="p">)</span><span class="w">
    
    </span><span class="n">results_list</span><span class="p">[[</span><span class="n">regimen_name</span><span class="p">]]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="w">
      </span><span class="n">sim_data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">sim_data</span><span class="p">,</span><span class="w">
      </span><span class="n">pta_64</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">pta_64</span><span class="p">,</span><span class="w">
      </span><span class="n">pta_32</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">pta_32</span><span class="p">,</span><span class="w">
      </span><span class="n">regimen</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">regimen</span><span class="w">
    </span><span class="p">)</span><span class="w">
  </span><span class="p">}</span><span class="w">
  
  </span><span class="n">results_list</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<h2 id="choosing-the-regimens-to-compare">Choosing the regimens to compare</h2>

<p>I keep the candidate regimens close to the current standard of care. The
result has to be a recommendation someone can act on, which usually means a
small change from what’s already done. The highest regimen here is only there
to show where the curve levels off. Check any candidate against the licensed
maximum daily dose before quoting it.</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">population</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">generate_virtual_population</span><span class="p">(</span><span class="n">n</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">2000</span><span class="p">,</span><span class="w"> </span><span class="n">age_range</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="m">12</span><span class="p">))</span><span class="w">

</span><span class="n">regimens</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="w">
  </span><span class="s2">"Standard_100mg_6h"</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="n">dose</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">100</span><span class="p">,</span><span class="w"> </span><span class="n">interval</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">6</span><span class="p">,</span><span class="w"> </span><span class="n">n_doses</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">4</span><span class="p">),</span><span class="w">
  </span><span class="s2">"High_125mg_6h"</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="n">dose</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">125</span><span class="p">,</span><span class="w"> </span><span class="n">interval</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">6</span><span class="p">,</span><span class="w"> </span><span class="n">n_doses</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">4</span><span class="p">),</span><span class="w">
  </span><span class="s2">"Extended_100mg_8h"</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="n">dose</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">100</span><span class="p">,</span><span class="w"> </span><span class="n">interval</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">8</span><span class="p">,</span><span class="w"> </span><span class="n">n_doses</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">3</span><span class="p">),</span><span class="w">
  </span><span class="s2">"Intensive_150mg_6h"</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="n">dose</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">150</span><span class="p">,</span><span class="w"> </span><span class="n">interval</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">6</span><span class="p">,</span><span class="w"> </span><span class="n">n_doses</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">4</span><span class="p">)</span><span class="w">
</span><span class="p">)</span><span class="w">

</span><span class="n">simulation_results</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">evaluate_dosing_regimens</span><span class="p">(</span><span class="n">mod</span><span class="p">,</span><span class="w"> </span><span class="n">population</span><span class="p">,</span><span class="w"> </span><span class="n">regimens</span><span class="p">)</span><span class="w">

</span><span class="n">pta_summary</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">map_dfr</span><span class="p">(</span><span class="nf">names</span><span class="p">(</span><span class="n">simulation_results</span><span class="p">),</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">regimen_name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">result</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">simulation_results</span><span class="p">[[</span><span class="n">regimen_name</span><span class="p">]]</span><span class="w">
  </span><span class="n">data.frame</span><span class="p">(</span><span class="w">
    </span><span class="n">Regimen</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">regimen_name</span><span class="p">,</span><span class="w">
    </span><span class="n">Dose_mgkg</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">result</span><span class="o">$</span><span class="n">regimen</span><span class="o">$</span><span class="n">dose</span><span class="p">,</span><span class="w">
    </span><span class="n">Interval_h</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">result</span><span class="o">$</span><span class="n">regimen</span><span class="o">$</span><span class="n">interval</span><span class="p">,</span><span class="w">
    </span><span class="n">PTA_64</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">mean</span><span class="p">(</span><span class="n">result</span><span class="o">$</span><span class="n">pta_64</span><span class="o">$</span><span class="n">PTA</span><span class="p">),</span><span class="w">
    </span><span class="n">PTA_32</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">mean</span><span class="p">(</span><span class="n">result</span><span class="o">$</span><span class="n">pta_32</span><span class="o">$</span><span class="n">PTA</span><span class="p">),</span><span class="w">
    </span><span class="n">Mean_Cmin</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">mean</span><span class="p">(</span><span class="n">result</span><span class="o">$</span><span class="n">pta_64</span><span class="o">$</span><span class="n">Cmin</span><span class="p">),</span><span class="w">
    </span><span class="n">Median_Cmin</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">median</span><span class="p">(</span><span class="n">result</span><span class="o">$</span><span class="n">pta_64</span><span class="o">$</span><span class="n">Cmin</span><span class="p">),</span><span class="w">
    </span><span class="n">P5_Cmin</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">quantile</span><span class="p">(</span><span class="n">result</span><span class="o">$</span><span class="n">pta_64</span><span class="o">$</span><span class="n">Cmin</span><span class="p">,</span><span class="w"> </span><span class="m">0.05</span><span class="p">),</span><span class="w">
    </span><span class="n">P95_Cmin</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">quantile</span><span class="p">(</span><span class="n">result</span><span class="o">$</span><span class="n">pta_64</span><span class="o">$</span><span class="n">Cmin</span><span class="p">,</span><span class="w"> </span><span class="m">0.95</span><span class="p">)</span><span class="w">
  </span><span class="p">)</span><span class="w">
</span><span class="p">})</span><span class="w">
</span></code></pre></div></div>

<h2 id="reading-the-output">Reading the output</h2>

<p>I look at three things, though usually only the first gets reported:</p>

<ul>
  <li>PTA against the target. The usual acceptance threshold is 90% of the
population, but that’s a convention. The threshold should depend on what
missing the target costs, which is different for a prophylactic indication
and a life-threatening infection.</li>
  <li>The shape of the dose-PTA curve. If attainment is still rising steeply at
the chosen dose, the regimen is fragile, and small errors in the assumed PK
cause large swings in attainment.</li>
  <li>Who fails. The overall percentage hides which subgroup misses. Plotting
trough against weight and renal function (<code class="language-plaintext highlighter-rouge">p2</code> below) can turn “88%
attainment” into “attainment is fine except in the augmented-clearance
subgroup”, and that leads to a different recommendation.</li>
</ul>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">p1</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">pta_summary</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
  </span><span class="n">pivot_longer</span><span class="p">(</span><span class="n">cols</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="n">PTA_64</span><span class="p">,</span><span class="w"> </span><span class="n">PTA_32</span><span class="p">),</span><span class="w"> </span><span class="n">names_to</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Target"</span><span class="p">,</span><span class="w"> </span><span class="n">values_to</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"PTA"</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
  </span><span class="n">mutate</span><span class="p">(</span><span class="n">Target</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ifelse</span><span class="p">(</span><span class="n">Target</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s2">"PTA_64"</span><span class="p">,</span><span class="w"> </span><span class="s2">"≥64 mg/L"</span><span class="p">,</span><span class="w"> </span><span class="s2">"≥32 mg/L"</span><span class="p">))</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
  </span><span class="n">ggplot</span><span class="p">(</span><span class="n">aes</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">reorder</span><span class="p">(</span><span class="n">Regimen</span><span class="p">,</span><span class="w"> </span><span class="n">PTA</span><span class="p">),</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PTA</span><span class="p">,</span><span class="w"> </span><span class="n">fill</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Target</span><span class="p">))</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">geom_bar</span><span class="p">(</span><span class="n">stat</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"identity"</span><span class="p">,</span><span class="w"> </span><span class="n">position</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"dodge"</span><span class="p">,</span><span class="w"> </span><span class="n">alpha</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.8</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">geom_hline</span><span class="p">(</span><span class="n">yintercept</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.9</span><span class="p">,</span><span class="w"> </span><span class="n">linetype</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"dashed"</span><span class="p">,</span><span class="w"> </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"red"</span><span class="p">,</span><span class="w"> </span><span class="n">size</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">annotate</span><span class="p">(</span><span class="s2">"text"</span><span class="p">,</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">2.5</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.92</span><span class="p">,</span><span class="w"> </span><span class="n">label</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Target: 90%"</span><span class="p">,</span><span class="w"> </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"red"</span><span class="p">,</span><span class="w"> </span><span class="n">fontface</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"bold"</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">scale_fill_viridis_d</span><span class="p">(</span><span class="n">begin</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.2</span><span class="p">,</span><span class="w"> </span><span class="n">end</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.8</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">labs</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Dosing Regimen"</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Probability of Target Attainment"</span><span class="p">,</span><span class="w">
       </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"PTA Comparison Across Dosing Regimens"</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">coord_flip</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">theme_minimal</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">theme</span><span class="p">(</span><span class="n">legend.position</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"bottom"</span><span class="p">)</span><span class="w">

</span><span class="n">p2</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">simulation_results</span><span class="o">$</span><span class="n">High_125mg_6h</span><span class="o">$</span><span class="n">pta_64</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
  </span><span class="n">ggplot</span><span class="p">(</span><span class="n">aes</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">WT</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Cmin</span><span class="p">,</span><span class="w"> </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">CRCL</span><span class="p">))</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">geom_point</span><span class="p">(</span><span class="n">alpha</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.5</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">geom_hline</span><span class="p">(</span><span class="n">yintercept</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">64</span><span class="p">,</span><span class="w"> </span><span class="n">linetype</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"dashed"</span><span class="p">,</span><span class="w"> </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"red"</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">geom_smooth</span><span class="p">(</span><span class="n">method</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"loess"</span><span class="p">,</span><span class="w"> </span><span class="n">se</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">,</span><span class="w"> </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"blue"</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">scale_color_viridis_c</span><span class="p">(</span><span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"CrCL\n(mL/min)"</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">labs</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Weight (kg)"</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Trough Concentration (mg/L)"</span><span class="p">,</span><span class="w">
       </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Cmin Distribution: 125 mg/kg q6h"</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">theme_minimal</span><span class="p">()</span><span class="w">

</span><span class="n">p3</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">map_dfr</span><span class="p">(</span><span class="nf">names</span><span class="p">(</span><span class="n">simulation_results</span><span class="p">),</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">regimen_name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">result</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">simulation_results</span><span class="p">[[</span><span class="n">regimen_name</span><span class="p">]]</span><span class="w">
  </span><span class="n">result</span><span class="o">$</span><span class="n">pta_64</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
    </span><span class="n">mutate</span><span class="p">(</span><span class="n">Regimen</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">regimen_name</span><span class="p">)</span><span class="w">
</span><span class="p">})</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
  </span><span class="n">ggplot</span><span class="p">(</span><span class="n">aes</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Regimen</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Cmin</span><span class="p">,</span><span class="w"> </span><span class="n">fill</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Regimen</span><span class="p">))</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">geom_violin</span><span class="p">(</span><span class="n">alpha</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.7</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">geom_boxplot</span><span class="p">(</span><span class="n">width</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.2</span><span class="p">,</span><span class="w"> </span><span class="n">outlier.alpha</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.3</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">geom_hline</span><span class="p">(</span><span class="n">yintercept</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">64</span><span class="p">,</span><span class="w"> </span><span class="n">linetype</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"dashed"</span><span class="p">,</span><span class="w"> </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"red"</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">scale_fill_viridis_d</span><span class="p">(</span><span class="n">begin</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.2</span><span class="p">,</span><span class="w"> </span><span class="n">end</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.8</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">labs</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Dosing Regimen"</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Trough Concentration (mg/L)"</span><span class="p">,</span><span class="w">
       </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Cmin Distribution Across Regimens"</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">theme_minimal</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">theme</span><span class="p">(</span><span class="n">legend.position</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"none"</span><span class="p">,</span><span class="w">
        </span><span class="n">axis.text.x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">element_text</span><span class="p">(</span><span class="n">angle</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">45</span><span class="p">,</span><span class="w"> </span><span class="n">hjust</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1</span><span class="p">))</span><span class="w">

</span><span class="n">p4</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">pta_summary</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
  </span><span class="n">ggplot</span><span class="p">(</span><span class="n">aes</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Dose_mgkg</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PTA_64</span><span class="p">,</span><span class="w"> </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">factor</span><span class="p">(</span><span class="n">Interval_h</span><span class="p">)))</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">geom_point</span><span class="p">(</span><span class="n">size</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">4</span><span class="p">,</span><span class="w"> </span><span class="n">alpha</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.8</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">geom_line</span><span class="p">(</span><span class="n">aes</span><span class="p">(</span><span class="n">group</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Interval_h</span><span class="p">),</span><span class="w"> </span><span class="n">alpha</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.5</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">geom_hline</span><span class="p">(</span><span class="n">yintercept</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.9</span><span class="p">,</span><span class="w"> </span><span class="n">linetype</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"dashed"</span><span class="p">,</span><span class="w"> </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"red"</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">scale_color_viridis_d</span><span class="p">(</span><span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Interval\n(hours)"</span><span class="p">,</span><span class="w"> </span><span class="n">begin</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.2</span><span class="p">,</span><span class="w"> </span><span class="n">end</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.8</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">labs</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Dose (mg/kg)"</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"PTA (≥64 mg/L)"</span><span class="p">,</span><span class="w">
       </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Dose-Response Relationship"</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">theme_minimal</span><span class="p">()</span><span class="w">

</span><span class="n">combined_plot</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="p">(</span><span class="n">p1</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">p2</span><span class="p">)</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="p">(</span><span class="n">p3</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">p4</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">plot_annotation</span><span class="p">(</span><span class="w">
    </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Monte Carlo Simulation for Piperacillin Dose Optimization"</span><span class="p">,</span><span class="w">
    </span><span class="n">subtitle</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Evaluation of multiple dosing regimens in pediatric population (n=2000)"</span><span class="p">,</span><span class="w">
    </span><span class="n">theme</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">theme</span><span class="p">(</span><span class="n">plot.title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">element_text</span><span class="p">(</span><span class="n">size</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">16</span><span class="p">,</span><span class="w"> </span><span class="n">face</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"bold"</span><span class="p">,</span><span class="w"> </span><span class="n">hjust</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.5</span><span class="p">),</span><span class="w">
                  </span><span class="n">plot.subtitle</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">element_text</span><span class="p">(</span><span class="n">size</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">12</span><span class="p">,</span><span class="w"> </span><span class="n">hjust</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.5</span><span class="p">,</span><span class="w"> </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"grey40"</span><span class="p">))</span><span class="w">
  </span><span class="p">)</span><span class="w">

</span><span class="n">print</span><span class="p">(</span><span class="n">combined_plot</span><span class="p">)</span><span class="w">

</span><span class="n">optimal_regimen</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">pta_summary</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
  </span><span class="n">filter</span><span class="p">(</span><span class="n">PTA_64</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="m">0.9</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
  </span><span class="n">arrange</span><span class="p">(</span><span class="n">Dose_mgkg</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
  </span><span class="n">slice_head</span><span class="p">(</span><span class="n">n</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w">

</span><span class="n">cat</span><span class="p">(</span><span class="s2">"\n=== Optimal Dosing Regimen ===\n"</span><span class="p">)</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="n">sprintf</span><span class="p">(</span><span class="s2">"Regimen: %s\n"</span><span class="p">,</span><span class="w"> </span><span class="n">optimal_regimen</span><span class="o">$</span><span class="n">Regimen</span><span class="p">))</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="n">sprintf</span><span class="p">(</span><span class="s2">"Dose: %d mg/kg\n"</span><span class="p">,</span><span class="w"> </span><span class="n">optimal_regimen</span><span class="o">$</span><span class="n">Dose_mgkg</span><span class="p">))</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="n">sprintf</span><span class="p">(</span><span class="s2">"Interval: %d hours\n"</span><span class="p">,</span><span class="w"> </span><span class="n">optimal_regimen</span><span class="o">$</span><span class="n">Interval_h</span><span class="p">))</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="n">sprintf</span><span class="p">(</span><span class="s2">"PTA (≥64 mg/L): %.1f%%\n"</span><span class="p">,</span><span class="w"> </span><span class="n">optimal_regimen</span><span class="o">$</span><span class="n">PTA_64</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="m">100</span><span class="p">))</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="n">sprintf</span><span class="p">(</span><span class="s2">"Mean Cmin: %.1f mg/L\n"</span><span class="p">,</span><span class="w"> </span><span class="n">optimal_regimen</span><span class="o">$</span><span class="n">Mean_Cmin</span><span class="p">))</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="n">sprintf</span><span class="p">(</span><span class="s2">"5th-95th Percentile Cmin: %.1f - %.1f mg/L\n"</span><span class="p">,</span><span class="w"> 
            </span><span class="n">optimal_regimen</span><span class="o">$</span><span class="n">P5_Cmin</span><span class="p">,</span><span class="w"> </span><span class="n">optimal_regimen</span><span class="o">$</span><span class="n">P95_Cmin</span><span class="p">))</span><span class="w">
</span></code></pre></div></div>]]></content><author><name>Wen Rui Tan</name></author><category term="Pharmacometrics" /><summary type="html"><![CDATA[Implementing Monte Carlo simulations to evaluate dosing regimens and determine optimal strategies for pediatric patients]]></summary></entry><entry><title type="html">Building Interactive R-Shiny Dashboards for Pharmacokinetic Simulation Visualization</title><link href="https://wrlog.github.io/2023/06/15/R-Shiny-Dashboard-for-PK-Simulation/" rel="alternate" type="text/html" title="Building Interactive R-Shiny Dashboards for Pharmacokinetic Simulation Visualization" /><published>2023-06-15T00:00:00-04:00</published><updated>2023-06-15T00:00:00-04:00</updated><id>https://wrlog.github.io/2023/06/15/R-Shiny-Dashboard-for-PK-Simulation</id><content type="html" xml:base="https://wrlog.github.io/2023/06/15/R-Shiny-Dashboard-for-PK-Simulation/"><![CDATA[<h2 id="why-a-dashboard">Why a dashboard</h2>

<p>If only the modeller can run a population model, it doesn’t do much for
patients. The people making dosing decisions can’t install NONMEM and
shouldn’t have to. A small interactive app lets them change the inputs they
already think in (weight, renal function, dose, interval) and see the result
straight away.</p>

<p>Two things decide whether an app like this gets used:</p>

<ul>
  <li>Use the inputs clinicians think in, not the model’s parameters. Nobody on a
ward wants to set a clearance. They want to enter a weight and a creatinine
clearance and see what happens to the trough.</li>
  <li>Show the spread. A single median profile makes the prediction look exact.
Showing variability across simulated patients is a fairer picture of what a
population model says, and usually more useful for a dosing decision.</li>
</ul>

<p>The app follows the usual Shiny layout: a <code class="language-plaintext highlighter-rouge">ui</code> object for the layout, a
<code class="language-plaintext highlighter-rouge">server</code> function with the simulation logic, and a reactive expression so the
model only re-runs when an input changes.</p>

<h2 id="the-code">The code</h2>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">library</span><span class="p">(</span><span class="n">shiny</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">shinydashboard</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">plotly</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">DT</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">dplyr</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">ggplot2</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">mrgsolve</span><span class="p">)</span><span class="w">

</span><span class="c1"># mrgsolve ships no piperacillin model; modlib() provides generic</span><span class="w">
</span><span class="c1"># structures (pk1, pk2, irm1-irm4, emax). Swap in your own model file</span><span class="w">
</span><span class="c1"># with mread("mymodel", project = "models").</span><span class="w">
</span><span class="n">mod</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">mread</span><span class="p">(</span><span class="s2">"pk2"</span><span class="p">,</span><span class="w"> </span><span class="n">modlib</span><span class="p">())</span><span class="w">

</span><span class="n">ui</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">dashboardPage</span><span class="p">(</span><span class="w">
  </span><span class="n">dashboardHeader</span><span class="p">(</span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Piperacillin PK Simulation Dashboard"</span><span class="p">),</span><span class="w">
  </span><span class="n">dashboardSidebar</span><span class="p">(</span><span class="w">
    </span><span class="n">sidebarMenu</span><span class="p">(</span><span class="w">
      </span><span class="n">menuItem</span><span class="p">(</span><span class="s2">"Simulation"</span><span class="p">,</span><span class="w"> </span><span class="n">tabName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"simulation"</span><span class="p">,</span><span class="w"> </span><span class="n">icon</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">icon</span><span class="p">(</span><span class="s2">"flask"</span><span class="p">)),</span><span class="w">
      </span><span class="n">menuItem</span><span class="p">(</span><span class="s2">"Dose Optimization"</span><span class="p">,</span><span class="w"> </span><span class="n">tabName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"dosing"</span><span class="p">,</span><span class="w"> </span><span class="n">icon</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">icon</span><span class="p">(</span><span class="s2">"pills"</span><span class="p">)),</span><span class="w">
      </span><span class="n">menuItem</span><span class="p">(</span><span class="s2">"Target Attainment"</span><span class="p">,</span><span class="w"> </span><span class="n">tabName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"pta"</span><span class="p">,</span><span class="w"> </span><span class="n">icon</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">icon</span><span class="p">(</span><span class="s2">"bullseye"</span><span class="p">)),</span><span class="w">
      </span><span class="n">menuItem</span><span class="p">(</span><span class="s2">"Patient Profiles"</span><span class="p">,</span><span class="w"> </span><span class="n">tabName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"profiles"</span><span class="p">,</span><span class="w"> </span><span class="n">icon</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">icon</span><span class="p">(</span><span class="s2">"user-md"</span><span class="p">))</span><span class="w">
    </span><span class="p">),</span><span class="w">
    </span><span class="n">hr</span><span class="p">(),</span><span class="w">
    </span><span class="n">h4</span><span class="p">(</span><span class="s2">"Patient Parameters"</span><span class="p">,</span><span class="w"> </span><span class="n">style</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"padding-left: 15px;"</span><span class="p">),</span><span class="w">
    </span><span class="n">numericInput</span><span class="p">(</span><span class="s2">"weight"</span><span class="p">,</span><span class="w"> </span><span class="s2">"Weight (kg)"</span><span class="p">,</span><span class="w"> </span><span class="n">value</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">20</span><span class="p">,</span><span class="w"> </span><span class="n">min</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">5</span><span class="p">,</span><span class="w"> </span><span class="n">max</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">100</span><span class="p">,</span><span class="w"> </span><span class="n">step</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.1</span><span class="p">),</span><span class="w">
    </span><span class="n">numericInput</span><span class="p">(</span><span class="s2">"age"</span><span class="p">,</span><span class="w"> </span><span class="s2">"Age (years)"</span><span class="p">,</span><span class="w"> </span><span class="n">value</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">5</span><span class="p">,</span><span class="w"> </span><span class="n">min</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.1</span><span class="p">,</span><span class="w"> </span><span class="n">max</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">18</span><span class="p">,</span><span class="w"> </span><span class="n">step</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.1</span><span class="p">),</span><span class="w">
    </span><span class="n">numericInput</span><span class="p">(</span><span class="s2">"crcl"</span><span class="p">,</span><span class="w"> </span><span class="s2">"Creatinine Clearance (mL/min)"</span><span class="p">,</span><span class="w"> </span><span class="n">value</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">80</span><span class="p">,</span><span class="w"> </span><span class="n">min</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">10</span><span class="p">,</span><span class="w"> </span><span class="n">max</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">200</span><span class="p">,</span><span class="w"> </span><span class="n">step</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1</span><span class="p">),</span><span class="w">
    </span><span class="n">selectInput</span><span class="p">(</span><span class="s2">"dose"</span><span class="p">,</span><span class="w"> </span><span class="s2">"Dose (mg/kg)"</span><span class="p">,</span><span class="w"> </span><span class="n">choices</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">50</span><span class="p">,</span><span class="w"> </span><span class="m">75</span><span class="p">,</span><span class="w"> </span><span class="m">100</span><span class="p">,</span><span class="w"> </span><span class="m">125</span><span class="p">,</span><span class="w"> </span><span class="m">150</span><span class="p">),</span><span class="w"> </span><span class="n">selected</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">100</span><span class="p">),</span><span class="w">
    </span><span class="n">selectInput</span><span class="p">(</span><span class="s2">"interval"</span><span class="p">,</span><span class="w"> </span><span class="s2">"Dosing Interval (hours)"</span><span class="p">,</span><span class="w"> </span><span class="n">choices</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">4</span><span class="p">,</span><span class="w"> </span><span class="m">6</span><span class="p">,</span><span class="w"> </span><span class="m">8</span><span class="p">,</span><span class="w"> </span><span class="m">12</span><span class="p">),</span><span class="w"> </span><span class="n">selected</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">6</span><span class="p">),</span><span class="w">
    </span><span class="n">numericInput</span><span class="p">(</span><span class="s2">"n_sims"</span><span class="p">,</span><span class="w"> </span><span class="s2">"Number of Simulations"</span><span class="p">,</span><span class="w"> </span><span class="n">value</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1000</span><span class="p">,</span><span class="w"> </span><span class="n">min</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">100</span><span class="p">,</span><span class="w"> </span><span class="n">max</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">10000</span><span class="p">,</span><span class="w"> </span><span class="n">step</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">100</span><span class="p">),</span><span class="w">
    </span><span class="n">actionButton</span><span class="p">(</span><span class="s2">"run_sim"</span><span class="p">,</span><span class="w"> </span><span class="s2">"Run Simulation"</span><span class="p">,</span><span class="w"> </span><span class="n">class</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"btn-primary"</span><span class="p">,</span><span class="w"> </span><span class="n">style</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"width: 100%;"</span><span class="p">)</span><span class="w">
  </span><span class="p">),</span><span class="w">
  </span><span class="n">dashboardBody</span><span class="p">(</span><span class="w">
    </span><span class="n">tags</span><span class="o">$</span><span class="n">head</span><span class="p">(</span><span class="w">
      </span><span class="n">tags</span><span class="o">$</span><span class="n">style</span><span class="p">(</span><span class="n">HTML</span><span class="p">(</span><span class="s2">"
        .content-wrapper { background-color: #f4f4f4; }
        .box { border-radius: 5px; }
        .info-box { border-radius: 5px; }
      "</span><span class="p">))</span><span class="w">
    </span><span class="p">),</span><span class="w">
    </span><span class="n">tabItems</span><span class="p">(</span><span class="w">
      </span><span class="n">tabItem</span><span class="p">(</span><span class="n">tabName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"simulation"</span><span class="p">,</span><span class="w">
        </span><span class="n">fluidRow</span><span class="p">(</span><span class="w">
          </span><span class="n">infoBox</span><span class="p">(</span><span class="s2">"AUC0-24"</span><span class="p">,</span><span class="w"> </span><span class="n">textOutput</span><span class="p">(</span><span class="s2">"auc"</span><span class="p">),</span><span class="w"> </span><span class="n">icon</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">icon</span><span class="p">(</span><span class="s2">"chart-area"</span><span class="p">),</span><span class="w"> </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"blue"</span><span class="p">,</span><span class="w"> </span><span class="n">width</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">4</span><span class="p">),</span><span class="w">
          </span><span class="n">infoBox</span><span class="p">(</span><span class="s2">"Cmax"</span><span class="p">,</span><span class="w"> </span><span class="n">textOutput</span><span class="p">(</span><span class="s2">"cmax"</span><span class="p">),</span><span class="w"> </span><span class="n">icon</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">icon</span><span class="p">(</span><span class="s2">"arrow-up"</span><span class="p">),</span><span class="w"> </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"green"</span><span class="p">,</span><span class="w"> </span><span class="n">width</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">4</span><span class="p">),</span><span class="w">
          </span><span class="n">infoBox</span><span class="p">(</span><span class="s2">"Cmin"</span><span class="p">,</span><span class="w"> </span><span class="n">textOutput</span><span class="p">(</span><span class="s2">"cmin"</span><span class="p">),</span><span class="w"> </span><span class="n">icon</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">icon</span><span class="p">(</span><span class="s2">"arrow-down"</span><span class="p">),</span><span class="w"> </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"red"</span><span class="p">,</span><span class="w"> </span><span class="n">width</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">4</span><span class="p">)</span><span class="w">
        </span><span class="p">),</span><span class="w">
        </span><span class="n">fluidRow</span><span class="p">(</span><span class="w">
          </span><span class="n">box</span><span class="p">(</span><span class="w">
            </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Concentration-Time Profile"</span><span class="p">,</span><span class="w"> </span><span class="n">status</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"primary"</span><span class="p">,</span><span class="w"> </span><span class="n">solidHeader</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">,</span><span class="w">
            </span><span class="n">width</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">12</span><span class="p">,</span><span class="w"> </span><span class="n">height</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">500</span><span class="p">,</span><span class="w">
            </span><span class="n">plotlyOutput</span><span class="p">(</span><span class="s2">"conc_plot"</span><span class="p">,</span><span class="w"> </span><span class="n">height</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"450px"</span><span class="p">)</span><span class="w">
          </span><span class="p">)</span><span class="w">
        </span><span class="p">),</span><span class="w">
        </span><span class="n">fluidRow</span><span class="p">(</span><span class="w">
          </span><span class="n">box</span><span class="p">(</span><span class="w">
            </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Simulation Summary Statistics"</span><span class="p">,</span><span class="w"> </span><span class="n">status</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"info"</span><span class="p">,</span><span class="w"> </span><span class="n">solidHeader</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">,</span><span class="w">
            </span><span class="n">width</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">12</span><span class="p">,</span><span class="w">
            </span><span class="n">DT</span><span class="o">::</span><span class="n">dataTableOutput</span><span class="p">(</span><span class="s2">"summary_table"</span><span class="p">)</span><span class="w">
          </span><span class="p">)</span><span class="w">
        </span><span class="p">)</span><span class="w">
      </span><span class="p">),</span><span class="w">
      </span><span class="n">tabItem</span><span class="p">(</span><span class="n">tabName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"dosing"</span><span class="p">,</span><span class="w">
        </span><span class="n">fluidRow</span><span class="p">(</span><span class="w">
          </span><span class="n">box</span><span class="p">(</span><span class="w">
            </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Dose-Exposure Relationship"</span><span class="p">,</span><span class="w"> </span><span class="n">status</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"primary"</span><span class="p">,</span><span class="w"> </span><span class="n">solidHeader</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">,</span><span class="w">
            </span><span class="n">width</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">6</span><span class="p">,</span><span class="w">
            </span><span class="n">plotlyOutput</span><span class="p">(</span><span class="s2">"dose_response"</span><span class="p">,</span><span class="w"> </span><span class="n">height</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"400px"</span><span class="p">)</span><span class="w">
          </span><span class="p">),</span><span class="w">
          </span><span class="n">box</span><span class="p">(</span><span class="w">
            </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Optimal Dosing Recommendations"</span><span class="p">,</span><span class="w"> </span><span class="n">status</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"success"</span><span class="p">,</span><span class="w"> </span><span class="n">solidHeader</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">,</span><span class="w">
            </span><span class="n">width</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">6</span><span class="p">,</span><span class="w">
            </span><span class="n">h4</span><span class="p">(</span><span class="s2">"Recommended Regimen:"</span><span class="p">,</span><span class="w"> </span><span class="n">style</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"color: #3c763d;"</span><span class="p">),</span><span class="w">
            </span><span class="n">verbatimTextOutput</span><span class="p">(</span><span class="s2">"optimal_dose"</span><span class="p">),</span><span class="w">
            </span><span class="n">hr</span><span class="p">(),</span><span class="w">
            </span><span class="n">h5</span><span class="p">(</span><span class="s2">"Rationale:"</span><span class="p">),</span><span class="w">
            </span><span class="n">textOutput</span><span class="p">(</span><span class="s2">"dose_rationale"</span><span class="p">)</span><span class="w">
          </span><span class="p">)</span><span class="w">
        </span><span class="p">),</span><span class="w">
        </span><span class="n">fluidRow</span><span class="p">(</span><span class="w">
          </span><span class="n">box</span><span class="p">(</span><span class="w">
            </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Dosing Regimen Comparison"</span><span class="p">,</span><span class="w"> </span><span class="n">status</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"warning"</span><span class="p">,</span><span class="w"> </span><span class="n">solidHeader</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">,</span><span class="w">
            </span><span class="n">width</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">12</span><span class="p">,</span><span class="w">
            </span><span class="n">plotlyOutput</span><span class="p">(</span><span class="s2">"regimen_comparison"</span><span class="p">,</span><span class="w"> </span><span class="n">height</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"400px"</span><span class="p">)</span><span class="w">
          </span><span class="p">)</span><span class="w">
        </span><span class="p">)</span><span class="w">
      </span><span class="p">),</span><span class="w">
      </span><span class="n">tabItem</span><span class="p">(</span><span class="n">tabName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"pta"</span><span class="p">,</span><span class="w">
        </span><span class="n">fluidRow</span><span class="p">(</span><span class="w">
          </span><span class="n">box</span><span class="p">(</span><span class="w">
            </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Probability of Target Attainment"</span><span class="p">,</span><span class="w"> </span><span class="n">status</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"primary"</span><span class="p">,</span><span class="w"> </span><span class="n">solidHeader</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">,</span><span class="w">
            </span><span class="n">width</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">12</span><span class="p">,</span><span class="w">
            </span><span class="n">plotlyOutput</span><span class="p">(</span><span class="s2">"pta_plot"</span><span class="p">,</span><span class="w"> </span><span class="n">height</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"500px"</span><span class="p">)</span><span class="w">
          </span><span class="p">)</span><span class="w">
        </span><span class="p">),</span><span class="w">
        </span><span class="n">fluidRow</span><span class="p">(</span><span class="w">
          </span><span class="n">box</span><span class="p">(</span><span class="w">
            </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"PTA Summary by Dosing Regimen"</span><span class="p">,</span><span class="w"> </span><span class="n">status</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"info"</span><span class="p">,</span><span class="w"> </span><span class="n">solidHeader</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">,</span><span class="w">
            </span><span class="n">width</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">12</span><span class="p">,</span><span class="w">
            </span><span class="n">DT</span><span class="o">::</span><span class="n">dataTableOutput</span><span class="p">(</span><span class="s2">"pta_table"</span><span class="p">)</span><span class="w">
          </span><span class="p">)</span><span class="w">
        </span><span class="p">)</span><span class="w">
      </span><span class="p">),</span><span class="w">
      </span><span class="n">tabItem</span><span class="p">(</span><span class="n">tabName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"profiles"</span><span class="p">,</span><span class="w">
        </span><span class="n">fluidRow</span><span class="p">(</span><span class="w">
          </span><span class="n">box</span><span class="p">(</span><span class="w">
            </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Virtual Patient Population"</span><span class="p">,</span><span class="w"> </span><span class="n">status</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"primary"</span><span class="p">,</span><span class="w"> </span><span class="n">solidHeader</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">,</span><span class="w">
            </span><span class="n">width</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">12</span><span class="p">,</span><span class="w">
            </span><span class="n">DT</span><span class="o">::</span><span class="n">dataTableOutput</span><span class="p">(</span><span class="s2">"patient_table"</span><span class="p">)</span><span class="w">
          </span><span class="p">)</span><span class="w">
        </span><span class="p">),</span><span class="w">
        </span><span class="n">fluidRow</span><span class="p">(</span><span class="w">
          </span><span class="n">box</span><span class="p">(</span><span class="w">
            </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Covariate Effects on PK Parameters"</span><span class="p">,</span><span class="w"> </span><span class="n">status</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"success"</span><span class="p">,</span><span class="w"> </span><span class="n">solidHeader</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">,</span><span class="w">
            </span><span class="n">width</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">6</span><span class="p">,</span><span class="w">
            </span><span class="n">plotlyOutput</span><span class="p">(</span><span class="s2">"covariate_cl"</span><span class="p">,</span><span class="w"> </span><span class="n">height</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"350px"</span><span class="p">)</span><span class="w">
          </span><span class="p">),</span><span class="w">
          </span><span class="n">box</span><span class="p">(</span><span class="w">
            </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Covariate Effects on Vd"</span><span class="p">,</span><span class="w"> </span><span class="n">status</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"success"</span><span class="p">,</span><span class="w"> </span><span class="n">solidHeader</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">,</span><span class="w">
            </span><span class="n">width</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">6</span><span class="p">,</span><span class="w">
            </span><span class="n">plotlyOutput</span><span class="p">(</span><span class="s2">"covariate_vd"</span><span class="p">,</span><span class="w"> </span><span class="n">height</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"350px"</span><span class="p">)</span><span class="w">
          </span><span class="p">)</span><span class="w">
        </span><span class="p">)</span><span class="w">
      </span><span class="p">)</span><span class="w">
    </span><span class="p">)</span><span class="w">
  </span><span class="p">)</span><span class="w">
</span><span class="p">)</span><span class="w">

</span><span class="n">server</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">input</span><span class="p">,</span><span class="w"> </span><span class="n">output</span><span class="p">,</span><span class="w"> </span><span class="n">session</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  
  </span><span class="n">sim_results</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">eventReactive</span><span class="p">(</span><span class="n">input</span><span class="o">$</span><span class="n">run_sim</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">withProgress</span><span class="p">(</span><span class="n">message</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Running simulations..."</span><span class="p">,</span><span class="w"> </span><span class="n">value</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="n">setProgress</span><span class="p">(</span><span class="m">0.2</span><span class="p">,</span><span class="w"> </span><span class="n">detail</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Preparing patient parameters"</span><span class="p">)</span><span class="w">
      
      </span><span class="n">patient_params</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">data.frame</span><span class="p">(</span><span class="w">
        </span><span class="n">ID</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">input</span><span class="o">$</span><span class="n">n_sims</span><span class="p">,</span><span class="w">
        </span><span class="n">WT</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">rnorm</span><span class="p">(</span><span class="n">input</span><span class="o">$</span><span class="n">n_sims</span><span class="p">,</span><span class="w"> </span><span class="n">input</span><span class="o">$</span><span class="n">weight</span><span class="p">,</span><span class="w"> </span><span class="n">input</span><span class="o">$</span><span class="n">weight</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="m">0.2</span><span class="p">),</span><span class="w">
        </span><span class="n">AGE</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">rnorm</span><span class="p">(</span><span class="n">input</span><span class="o">$</span><span class="n">n_sims</span><span class="p">,</span><span class="w"> </span><span class="n">input</span><span class="o">$</span><span class="n">age</span><span class="p">,</span><span class="w"> </span><span class="n">input</span><span class="o">$</span><span class="n">age</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="m">0.15</span><span class="p">),</span><span class="w">
        </span><span class="n">CRCL</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">rnorm</span><span class="p">(</span><span class="n">input</span><span class="o">$</span><span class="n">n_sims</span><span class="p">,</span><span class="w"> </span><span class="n">input</span><span class="o">$</span><span class="n">crcl</span><span class="p">,</span><span class="w"> </span><span class="n">input</span><span class="o">$</span><span class="n">crcl</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="m">0.25</span><span class="p">)</span><span class="w">
      </span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
        </span><span class="n">mutate</span><span class="p">(</span><span class="w">
          </span><span class="n">WT</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">pmax</span><span class="p">(</span><span class="m">5</span><span class="p">,</span><span class="w"> </span><span class="n">pmin</span><span class="p">(</span><span class="m">100</span><span class="p">,</span><span class="w"> </span><span class="n">WT</span><span class="p">)),</span><span class="w">
          </span><span class="n">AGE</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">pmax</span><span class="p">(</span><span class="m">0.1</span><span class="p">,</span><span class="w"> </span><span class="n">pmin</span><span class="p">(</span><span class="m">18</span><span class="p">,</span><span class="w"> </span><span class="n">AGE</span><span class="p">)),</span><span class="w">
          </span><span class="n">CRCL</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">pmax</span><span class="p">(</span><span class="m">10</span><span class="p">,</span><span class="w"> </span><span class="n">pmin</span><span class="p">(</span><span class="m">200</span><span class="p">,</span><span class="w"> </span><span class="n">CRCL</span><span class="p">))</span><span class="w">
        </span><span class="p">)</span><span class="w">
      
      </span><span class="n">setProgress</span><span class="p">(</span><span class="m">0.4</span><span class="p">,</span><span class="w"> </span><span class="n">detail</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Simulating PK profiles"</span><span class="p">)</span><span class="w">
      
      </span><span class="n">dose_mg</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">input</span><span class="o">$</span><span class="n">dose</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">patient_params</span><span class="o">$</span><span class="n">WT</span><span class="w">
      
      </span><span class="n">sim_data</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">lapply</span><span class="p">(</span><span class="m">1</span><span class="o">:</span><span class="n">input</span><span class="o">$</span><span class="n">n_sims</span><span class="p">,</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">i</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="n">ev</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ev</span><span class="p">(</span><span class="n">amt</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">dose_mg</span><span class="p">[</span><span class="n">i</span><span class="p">],</span><span class="w"> </span><span class="n">ii</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">as.numeric</span><span class="p">(</span><span class="n">input</span><span class="o">$</span><span class="n">interval</span><span class="p">),</span><span class="w"> </span><span class="n">addl</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">3</span><span class="p">)</span><span class="w">
        </span><span class="n">out</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">mod</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
          </span><span class="n">param</span><span class="p">(</span><span class="n">CL</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">2.5</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="p">(</span><span class="n">patient_params</span><span class="o">$</span><span class="n">CRCL</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">/</span><span class="m">80</span><span class="p">)</span><span class="o">^</span><span class="m">0.75</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="p">(</span><span class="n">patient_params</span><span class="o">$</span><span class="n">WT</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">/</span><span class="m">20</span><span class="p">)</span><span class="o">^</span><span class="m">0.75</span><span class="p">,</span><span class="w">
                </span><span class="n">V</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">15</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="p">(</span><span class="n">patient_params</span><span class="o">$</span><span class="n">WT</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">/</span><span class="m">20</span><span class="p">))</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
          </span><span class="n">ev</span><span class="p">(</span><span class="n">ev</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
          </span><span class="n">mrgsim</span><span class="p">(</span><span class="n">end</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">24</span><span class="p">,</span><span class="w"> </span><span class="n">delta</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.5</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
          </span><span class="n">as.data.frame</span><span class="p">()</span><span class="w">
        </span><span class="n">out</span><span class="o">$</span><span class="n">ID</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">i</span><span class="w">
        </span><span class="n">out</span><span class="o">$</span><span class="n">WT</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">patient_params</span><span class="o">$</span><span class="n">WT</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="w">
        </span><span class="n">out</span><span class="o">$</span><span class="n">CRCL</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">patient_params</span><span class="o">$</span><span class="n">CRCL</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="w">
        </span><span class="nf">return</span><span class="p">(</span><span class="n">out</span><span class="p">)</span><span class="w">
      </span><span class="p">})</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
        </span><span class="n">bind_rows</span><span class="p">()</span><span class="w">
      
      </span><span class="n">setProgress</span><span class="p">(</span><span class="m">0.8</span><span class="p">,</span><span class="w"> </span><span class="n">detail</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Calculating PK metrics"</span><span class="p">)</span><span class="w">
      
      </span><span class="n">pk_metrics</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sim_data</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
        </span><span class="n">group_by</span><span class="p">(</span><span class="n">ID</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
        </span><span class="n">summarise</span><span class="p">(</span><span class="w">
          </span><span class="n">AUC</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">pracma</span><span class="o">::</span><span class="n">trapz</span><span class="p">(</span><span class="n">time</span><span class="p">,</span><span class="w"> </span><span class="n">CP</span><span class="p">),</span><span class="w">
          </span><span class="n">Cmax</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">max</span><span class="p">(</span><span class="n">CP</span><span class="p">,</span><span class="w"> </span><span class="n">na.rm</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">),</span><span class="w">
          </span><span class="n">Cmin</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">min</span><span class="p">(</span><span class="n">CP</span><span class="p">[</span><span class="n">time</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="m">18</span><span class="p">],</span><span class="w"> </span><span class="n">na.rm</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">),</span><span class="w">
          </span><span class="n">Tmax</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">time</span><span class="p">[</span><span class="n">which.max</span><span class="p">(</span><span class="n">CP</span><span class="p">)],</span><span class="w">
          </span><span class="n">WT</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">first</span><span class="p">(</span><span class="n">WT</span><span class="p">),</span><span class="w">
          </span><span class="n">CRCL</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">first</span><span class="p">(</span><span class="n">CRCL</span><span class="p">)</span><span class="w">
        </span><span class="p">)</span><span class="w">
      
      </span><span class="n">setProgress</span><span class="p">(</span><span class="m">1.0</span><span class="p">,</span><span class="w"> </span><span class="n">detail</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Complete"</span><span class="p">)</span><span class="w">
      
      </span><span class="nf">list</span><span class="p">(</span><span class="n">sim_data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">sim_data</span><span class="p">,</span><span class="w"> </span><span class="n">pk_metrics</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">pk_metrics</span><span class="p">,</span><span class="w"> </span><span class="n">patient_params</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">patient_params</span><span class="p">)</span><span class="w">
    </span><span class="p">})</span><span class="w">
  </span><span class="p">})</span><span class="w">
  
  </span><span class="n">output</span><span class="o">$</span><span class="n">conc_plot</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">renderPlotly</span><span class="p">({</span><span class="w">
    </span><span class="n">results</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sim_results</span><span class="p">()</span><span class="w">
    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nf">is.null</span><span class="p">(</span><span class="n">results</span><span class="p">))</span><span class="w"> </span><span class="nf">return</span><span class="p">(</span><span class="kc">NULL</span><span class="p">)</span><span class="w">
    
    </span><span class="n">p</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">results</span><span class="o">$</span><span class="n">sim_data</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">filter</span><span class="p">(</span><span class="n">ID</span><span class="w"> </span><span class="o">&lt;=</span><span class="w"> </span><span class="m">10</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">ggplot</span><span class="p">(</span><span class="n">aes</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">time</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">CP</span><span class="p">,</span><span class="w"> </span><span class="n">group</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ID</span><span class="p">,</span><span class="w"> </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">factor</span><span class="p">(</span><span class="n">ID</span><span class="p">)))</span><span class="w"> </span><span class="o">+</span><span class="w">
      </span><span class="n">geom_line</span><span class="p">(</span><span class="n">alpha</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.7</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
      </span><span class="n">geom_hline</span><span class="p">(</span><span class="n">yintercept</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">64</span><span class="p">,</span><span class="w"> </span><span class="n">linetype</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"dashed"</span><span class="p">,</span><span class="w"> </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"red"</span><span class="p">,</span><span class="w"> </span><span class="n">alpha</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.7</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
      </span><span class="n">annotate</span><span class="p">(</span><span class="s2">"text"</span><span class="p">,</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">20</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">70</span><span class="p">,</span><span class="w"> </span><span class="n">label</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Target: 64 mg/L"</span><span class="p">,</span><span class="w"> </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"red"</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
      </span><span class="n">labs</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Time (hours)"</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Concentration (mg/L)"</span><span class="p">,</span><span class="w">
           </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Concentration-Time Profiles (Sample of 10 patients)"</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
      </span><span class="n">theme_minimal</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w">
      </span><span class="n">theme</span><span class="p">(</span><span class="n">legend.position</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"none"</span><span class="p">)</span><span class="w">
    
    </span><span class="n">ggplotly</span><span class="p">(</span><span class="n">p</span><span class="p">,</span><span class="w"> </span><span class="n">tooltip</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="s2">"time"</span><span class="p">,</span><span class="w"> </span><span class="s2">"CP"</span><span class="p">,</span><span class="w"> </span><span class="s2">"ID"</span><span class="p">))</span><span class="w">
  </span><span class="p">})</span><span class="w">
  
  </span><span class="n">output</span><span class="o">$</span><span class="n">auc</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">renderText</span><span class="p">({</span><span class="w">
    </span><span class="n">results</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sim_results</span><span class="p">()</span><span class="w">
    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nf">is.null</span><span class="p">(</span><span class="n">results</span><span class="p">))</span><span class="w"> </span><span class="nf">return</span><span class="p">(</span><span class="s2">"--"</span><span class="p">)</span><span class="w">
    </span><span class="n">paste0</span><span class="p">(</span><span class="nf">round</span><span class="p">(</span><span class="n">mean</span><span class="p">(</span><span class="n">results</span><span class="o">$</span><span class="n">pk_metrics</span><span class="o">$</span><span class="n">AUC</span><span class="p">,</span><span class="w"> </span><span class="n">na.rm</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">),</span><span class="w"> </span><span class="m">1</span><span class="p">),</span><span class="w"> </span><span class="s2">" mg·h/L"</span><span class="p">)</span><span class="w">
  </span><span class="p">})</span><span class="w">
  
  </span><span class="n">output</span><span class="o">$</span><span class="n">cmax</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">renderText</span><span class="p">({</span><span class="w">
    </span><span class="n">results</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sim_results</span><span class="p">()</span><span class="w">
    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nf">is.null</span><span class="p">(</span><span class="n">results</span><span class="p">))</span><span class="w"> </span><span class="nf">return</span><span class="p">(</span><span class="s2">"--"</span><span class="p">)</span><span class="w">
    </span><span class="n">paste0</span><span class="p">(</span><span class="nf">round</span><span class="p">(</span><span class="n">mean</span><span class="p">(</span><span class="n">results</span><span class="o">$</span><span class="n">pk_metrics</span><span class="o">$</span><span class="n">Cmax</span><span class="p">,</span><span class="w"> </span><span class="n">na.rm</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">),</span><span class="w"> </span><span class="m">1</span><span class="p">),</span><span class="w"> </span><span class="s2">" mg/L"</span><span class="p">)</span><span class="w">
  </span><span class="p">})</span><span class="w">
  
  </span><span class="n">output</span><span class="o">$</span><span class="n">cmin</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">renderText</span><span class="p">({</span><span class="w">
    </span><span class="n">results</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sim_results</span><span class="p">()</span><span class="w">
    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nf">is.null</span><span class="p">(</span><span class="n">results</span><span class="p">))</span><span class="w"> </span><span class="nf">return</span><span class="p">(</span><span class="s2">"--"</span><span class="p">)</span><span class="w">
    </span><span class="n">paste0</span><span class="p">(</span><span class="nf">round</span><span class="p">(</span><span class="n">mean</span><span class="p">(</span><span class="n">results</span><span class="o">$</span><span class="n">pk_metrics</span><span class="o">$</span><span class="n">Cmin</span><span class="p">,</span><span class="w"> </span><span class="n">na.rm</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">),</span><span class="w"> </span><span class="m">1</span><span class="p">),</span><span class="w"> </span><span class="s2">" mg/L"</span><span class="p">)</span><span class="w">
  </span><span class="p">})</span><span class="w">
  
  </span><span class="n">output</span><span class="o">$</span><span class="n">summary_table</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">DT</span><span class="o">::</span><span class="n">renderDataTable</span><span class="p">({</span><span class="w">
    </span><span class="n">results</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sim_results</span><span class="p">()</span><span class="w">
    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nf">is.null</span><span class="p">(</span><span class="n">results</span><span class="p">))</span><span class="w"> </span><span class="nf">return</span><span class="p">(</span><span class="kc">NULL</span><span class="p">)</span><span class="w">
    
    </span><span class="n">results</span><span class="o">$</span><span class="n">pk_metrics</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">summarise</span><span class="p">(</span><span class="w">
        </span><span class="n">Metric</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="s2">"AUC (mg·h/L)"</span><span class="p">,</span><span class="w"> </span><span class="s2">"Cmax (mg/L)"</span><span class="p">,</span><span class="w"> </span><span class="s2">"Cmin (mg/L)"</span><span class="p">),</span><span class="w">
        </span><span class="n">Mean</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="n">mean</span><span class="p">(</span><span class="n">AUC</span><span class="p">),</span><span class="w"> </span><span class="n">mean</span><span class="p">(</span><span class="n">Cmax</span><span class="p">),</span><span class="w"> </span><span class="n">mean</span><span class="p">(</span><span class="n">Cmin</span><span class="p">)),</span><span class="w">
        </span><span class="n">Median</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="n">median</span><span class="p">(</span><span class="n">AUC</span><span class="p">),</span><span class="w"> </span><span class="n">median</span><span class="p">(</span><span class="n">Cmax</span><span class="p">),</span><span class="w"> </span><span class="n">median</span><span class="p">(</span><span class="n">Cmin</span><span class="p">)),</span><span class="w">
        </span><span class="n">`5th Percentile`</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="n">quantile</span><span class="p">(</span><span class="n">AUC</span><span class="p">,</span><span class="w"> </span><span class="m">0.05</span><span class="p">),</span><span class="w"> </span><span class="n">quantile</span><span class="p">(</span><span class="n">Cmax</span><span class="p">,</span><span class="w"> </span><span class="m">0.05</span><span class="p">),</span><span class="w"> </span><span class="n">quantile</span><span class="p">(</span><span class="n">Cmin</span><span class="p">,</span><span class="w"> </span><span class="m">0.05</span><span class="p">)),</span><span class="w">
        </span><span class="n">`95th Percentile`</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="n">quantile</span><span class="p">(</span><span class="n">AUC</span><span class="p">,</span><span class="w"> </span><span class="m">0.95</span><span class="p">),</span><span class="w"> </span><span class="n">quantile</span><span class="p">(</span><span class="n">Cmax</span><span class="p">,</span><span class="w"> </span><span class="m">0.95</span><span class="p">),</span><span class="w"> </span><span class="n">quantile</span><span class="p">(</span><span class="n">Cmin</span><span class="p">,</span><span class="w"> </span><span class="m">0.95</span><span class="p">))</span><span class="w">
      </span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">DT</span><span class="o">::</span><span class="n">datatable</span><span class="p">(</span><span class="n">options</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="n">pageLength</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">10</span><span class="p">,</span><span class="w"> </span><span class="n">dom</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'t'</span><span class="p">),</span><span class="w"> </span><span class="n">rownames</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">FALSE</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">DT</span><span class="o">::</span><span class="n">formatRound</span><span class="p">(</span><span class="n">columns</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">2</span><span class="o">:</span><span class="m">5</span><span class="p">,</span><span class="w"> </span><span class="n">digits</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">2</span><span class="p">)</span><span class="w">
  </span><span class="p">})</span><span class="w">
  
  </span><span class="n">output</span><span class="o">$</span><span class="n">dose_response</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">renderPlotly</span><span class="p">({</span><span class="w">
    </span><span class="n">results</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sim_results</span><span class="p">()</span><span class="w">
    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nf">is.null</span><span class="p">(</span><span class="n">results</span><span class="p">))</span><span class="w"> </span><span class="nf">return</span><span class="p">(</span><span class="kc">NULL</span><span class="p">)</span><span class="w">
    
    </span><span class="n">dose_range</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">seq</span><span class="p">(</span><span class="m">50</span><span class="p">,</span><span class="w"> </span><span class="m">150</span><span class="p">,</span><span class="w"> </span><span class="n">by</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">25</span><span class="p">)</span><span class="w">
    </span><span class="n">dose_effects</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">data.frame</span><span class="p">(</span><span class="w">
      </span><span class="n">Dose</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">rep</span><span class="p">(</span><span class="n">dose_range</span><span class="p">,</span><span class="w"> </span><span class="n">each</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">input</span><span class="o">$</span><span class="n">n_sims</span><span class="p">),</span><span class="w">
      </span><span class="n">AUC</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">NA</span><span class="p">,</span><span class="w">
      </span><span class="n">Cmin</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">NA</span><span class="w">
    </span><span class="p">)</span><span class="w">
    
    </span><span class="n">p</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">results</span><span class="o">$</span><span class="n">pk_metrics</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">ggplot</span><span class="p">(</span><span class="n">aes</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">WT</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">AUC</span><span class="p">,</span><span class="w"> </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">CRCL</span><span class="p">))</span><span class="w"> </span><span class="o">+</span><span class="w">
      </span><span class="n">geom_point</span><span class="p">(</span><span class="n">alpha</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.6</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
      </span><span class="n">geom_smooth</span><span class="p">(</span><span class="n">method</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"loess"</span><span class="p">,</span><span class="w"> </span><span class="n">se</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">,</span><span class="w"> </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"blue"</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
      </span><span class="n">scale_color_viridis_c</span><span class="p">(</span><span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"CrCL\n(mL/min)"</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
      </span><span class="n">labs</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Weight (kg)"</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"AUC (mg·h/L)"</span><span class="p">,</span><span class="w">
           </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"AUC vs Body Weight (colored by Creatinine Clearance)"</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
      </span><span class="n">theme_minimal</span><span class="p">()</span><span class="w">
    
    </span><span class="n">ggplotly</span><span class="p">(</span><span class="n">p</span><span class="p">)</span><span class="w">
  </span><span class="p">})</span><span class="w">
  
  </span><span class="n">output</span><span class="o">$</span><span class="n">optimal_dose</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">renderText</span><span class="p">({</span><span class="w">
    </span><span class="n">results</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sim_results</span><span class="p">()</span><span class="w">
    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nf">is.null</span><span class="p">(</span><span class="n">results</span><span class="p">))</span><span class="w"> </span><span class="nf">return</span><span class="p">(</span><span class="s2">"Run simulation first"</span><span class="p">)</span><span class="w">
    
    </span><span class="n">target_pta</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">results</span><span class="o">$</span><span class="n">pk_metrics</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">mutate</span><span class="p">(</span><span class="n">PTA</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ifelse</span><span class="p">(</span><span class="n">Cmin</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="m">64</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="m">0</span><span class="p">))</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">summarise</span><span class="p">(</span><span class="n">PTA</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">mean</span><span class="p">(</span><span class="n">PTA</span><span class="p">))</span><span class="w">
    
    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">target_pta</span><span class="o">$</span><span class="n">PTA</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="m">0.9</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="n">paste0</span><span class="p">(</span><span class="n">input</span><span class="o">$</span><span class="n">dose</span><span class="p">,</span><span class="w"> </span><span class="s2">" mg/kg every "</span><span class="p">,</span><span class="w"> </span><span class="n">input</span><span class="o">$</span><span class="n">interval</span><span class="p">,</span><span class="w"> </span><span class="s2">" hours\n"</span><span class="p">,</span><span class="w">
             </span><span class="s2">"PTA: "</span><span class="p">,</span><span class="w"> </span><span class="nf">round</span><span class="p">(</span><span class="n">target_pta</span><span class="o">$</span><span class="n">PTA</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="m">100</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="p">),</span><span class="w"> </span><span class="s2">"%"</span><span class="p">)</span><span class="w">
    </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="n">recommended_dose</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">input</span><span class="o">$</span><span class="n">dose</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="m">1.25</span><span class="w">
      </span><span class="n">paste0</span><span class="p">(</span><span class="s2">"Consider: "</span><span class="p">,</span><span class="w"> </span><span class="nf">round</span><span class="p">(</span><span class="n">recommended_dose</span><span class="p">,</span><span class="w"> </span><span class="m">0</span><span class="p">),</span><span class="w"> </span><span class="s2">" mg/kg every "</span><span class="p">,</span><span class="w"> </span><span class="n">input</span><span class="o">$</span><span class="n">interval</span><span class="p">,</span><span class="w"> </span><span class="s2">" hours\n"</span><span class="p">,</span><span class="w">
             </span><span class="s2">"Current PTA: "</span><span class="p">,</span><span class="w"> </span><span class="nf">round</span><span class="p">(</span><span class="n">target_pta</span><span class="o">$</span><span class="n">PTA</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="m">100</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="p">),</span><span class="w"> </span><span class="s2">"%"</span><span class="p">)</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">})</span><span class="w">
  
  </span><span class="n">output</span><span class="o">$</span><span class="n">pta_plot</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">renderPlotly</span><span class="p">({</span><span class="w">
    </span><span class="n">results</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sim_results</span><span class="p">()</span><span class="w">
    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nf">is.null</span><span class="p">(</span><span class="n">results</span><span class="p">))</span><span class="w"> </span><span class="nf">return</span><span class="p">(</span><span class="kc">NULL</span><span class="p">)</span><span class="w">
    
    </span><span class="n">pta_data</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">results</span><span class="o">$</span><span class="n">pk_metrics</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">mutate</span><span class="p">(</span><span class="w">
        </span><span class="n">PTA_64</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ifelse</span><span class="p">(</span><span class="n">Cmin</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="m">64</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="m">0</span><span class="p">),</span><span class="w">
        </span><span class="n">PTA_32</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ifelse</span><span class="p">(</span><span class="n">Cmin</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="m">32</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="m">0</span><span class="p">)</span><span class="w">
      </span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">group_by</span><span class="p">(</span><span class="n">WT</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">cut</span><span class="p">(</span><span class="n">WT</span><span class="p">,</span><span class="w"> </span><span class="n">breaks</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">seq</span><span class="p">(</span><span class="m">5</span><span class="p">,</span><span class="w"> </span><span class="m">100</span><span class="p">,</span><span class="w"> </span><span class="n">by</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">10</span><span class="p">)))</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">summarise</span><span class="p">(</span><span class="w">
        </span><span class="n">PTA_64</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">mean</span><span class="p">(</span><span class="n">PTA_64</span><span class="p">),</span><span class="w">
        </span><span class="n">PTA_32</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">mean</span><span class="p">(</span><span class="n">PTA_32</span><span class="p">),</span><span class="w">
        </span><span class="n">.groups</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'drop'</span><span class="w">
      </span><span class="p">)</span><span class="w">
    
    </span><span class="n">p</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">pta_data</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">pivot_longer</span><span class="p">(</span><span class="n">cols</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="n">PTA_64</span><span class="p">,</span><span class="w"> </span><span class="n">PTA_32</span><span class="p">),</span><span class="w"> </span><span class="n">names_to</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Target"</span><span class="p">,</span><span class="w"> </span><span class="n">values_to</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"PTA"</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">ggplot</span><span class="p">(</span><span class="n">aes</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">WT</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PTA</span><span class="p">,</span><span class="w"> </span><span class="n">fill</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Target</span><span class="p">))</span><span class="w"> </span><span class="o">+</span><span class="w">
      </span><span class="n">geom_bar</span><span class="p">(</span><span class="n">stat</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"identity"</span><span class="p">,</span><span class="w"> </span><span class="n">position</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"dodge"</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
      </span><span class="n">geom_hline</span><span class="p">(</span><span class="n">yintercept</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.9</span><span class="p">,</span><span class="w"> </span><span class="n">linetype</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"dashed"</span><span class="p">,</span><span class="w"> </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"red"</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
      </span><span class="n">scale_fill_manual</span><span class="p">(</span><span class="n">values</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="s2">"PTA_64"</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"#667eea"</span><span class="p">,</span><span class="w"> </span><span class="s2">"PTA_32"</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"#764ba2"</span><span class="p">),</span><span class="w">
                       </span><span class="n">labels</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="s2">"PTA_64"</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"≥64 mg/L"</span><span class="p">,</span><span class="w"> </span><span class="s2">"PTA_32"</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"≥32 mg/L"</span><span class="p">))</span><span class="w"> </span><span class="o">+</span><span class="w">
      </span><span class="n">labs</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Weight Category (kg)"</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Probability of Target Attainment"</span><span class="p">,</span><span class="w">
           </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"PTA by Weight Category"</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
      </span><span class="n">theme_minimal</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w">
      </span><span class="n">theme</span><span class="p">(</span><span class="n">axis.text.x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">element_text</span><span class="p">(</span><span class="n">angle</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">45</span><span class="p">,</span><span class="w"> </span><span class="n">hjust</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1</span><span class="p">))</span><span class="w">
    
    </span><span class="n">ggplotly</span><span class="p">(</span><span class="n">p</span><span class="p">)</span><span class="w">
  </span><span class="p">})</span><span class="w">
  
  </span><span class="n">output</span><span class="o">$</span><span class="n">patient_table</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">DT</span><span class="o">::</span><span class="n">renderDataTable</span><span class="p">({</span><span class="w">
    </span><span class="n">results</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sim_results</span><span class="p">()</span><span class="w">
    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nf">is.null</span><span class="p">(</span><span class="n">results</span><span class="p">))</span><span class="w"> </span><span class="nf">return</span><span class="p">(</span><span class="kc">NULL</span><span class="p">)</span><span class="w">
    
    </span><span class="n">results</span><span class="o">$</span><span class="n">patient_params</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">DT</span><span class="o">::</span><span class="n">datatable</span><span class="p">(</span><span class="n">options</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="n">pageLength</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">15</span><span class="p">,</span><span class="w"> </span><span class="n">scrollX</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">))</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">DT</span><span class="o">::</span><span class="n">formatRound</span><span class="p">(</span><span class="n">columns</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="s2">"WT"</span><span class="p">,</span><span class="w"> </span><span class="s2">"AGE"</span><span class="p">,</span><span class="w"> </span><span class="s2">"CRCL"</span><span class="p">),</span><span class="w"> </span><span class="n">digits</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">2</span><span class="p">)</span><span class="w">
  </span><span class="p">})</span><span class="w">
  
  </span><span class="n">output</span><span class="o">$</span><span class="n">covariate_cl</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">renderPlotly</span><span class="p">({</span><span class="w">
    </span><span class="n">results</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sim_results</span><span class="p">()</span><span class="w">
    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nf">is.null</span><span class="p">(</span><span class="n">results</span><span class="p">))</span><span class="w"> </span><span class="nf">return</span><span class="p">(</span><span class="kc">NULL</span><span class="p">)</span><span class="w">
    
    </span><span class="n">p</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">results</span><span class="o">$</span><span class="n">pk_metrics</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">ggplot</span><span class="p">(</span><span class="n">aes</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">CRCL</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">AUC</span><span class="p">,</span><span class="w"> </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">WT</span><span class="p">))</span><span class="w"> </span><span class="o">+</span><span class="w">
      </span><span class="n">geom_point</span><span class="p">(</span><span class="n">alpha</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.6</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
      </span><span class="n">geom_smooth</span><span class="p">(</span><span class="n">method</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"lm"</span><span class="p">,</span><span class="w"> </span><span class="n">se</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">,</span><span class="w"> </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"blue"</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
      </span><span class="n">scale_color_viridis_c</span><span class="p">(</span><span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Weight\n(kg)"</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
      </span><span class="n">labs</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Creatinine Clearance (mL/min)"</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"AUC (mg·h/L)"</span><span class="p">,</span><span class="w">
           </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Effect of Renal Function on Exposure"</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
      </span><span class="n">theme_minimal</span><span class="p">()</span><span class="w">
    
    </span><span class="n">ggplotly</span><span class="p">(</span><span class="n">p</span><span class="p">)</span><span class="w">
  </span><span class="p">})</span><span class="w">
  
  </span><span class="n">output</span><span class="o">$</span><span class="n">covariate_vd</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">renderPlotly</span><span class="p">({</span><span class="w">
    </span><span class="n">results</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sim_results</span><span class="p">()</span><span class="w">
    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nf">is.null</span><span class="p">(</span><span class="n">results</span><span class="p">))</span><span class="w"> </span><span class="nf">return</span><span class="p">(</span><span class="kc">NULL</span><span class="p">)</span><span class="w">
    
    </span><span class="n">p</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">results</span><span class="o">$</span><span class="n">pk_metrics</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">ggplot</span><span class="p">(</span><span class="n">aes</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">WT</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Cmax</span><span class="p">,</span><span class="w"> </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">CRCL</span><span class="p">))</span><span class="w"> </span><span class="o">+</span><span class="w">
      </span><span class="n">geom_point</span><span class="p">(</span><span class="n">alpha</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.6</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
      </span><span class="n">geom_smooth</span><span class="p">(</span><span class="n">method</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"loess"</span><span class="p">,</span><span class="w"> </span><span class="n">se</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">,</span><span class="w"> </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"green"</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
      </span><span class="n">scale_color_viridis_c</span><span class="p">(</span><span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"CrCL\n(mL/min)"</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
      </span><span class="n">labs</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Weight (kg)"</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Cmax (mg/L)"</span><span class="p">,</span><span class="w">
           </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Effect of Body Size on Peak Concentration"</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
      </span><span class="n">theme_minimal</span><span class="p">()</span><span class="w">
    
    </span><span class="n">ggplotly</span><span class="p">(</span><span class="n">p</span><span class="p">)</span><span class="w">
  </span><span class="p">})</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="n">shinyApp</span><span class="p">(</span><span class="n">ui</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ui</span><span class="p">,</span><span class="w"> </span><span class="n">server</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">server</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>]]></content><author><name>Wen Rui Tan</name></author><category term="R" /><summary type="html"><![CDATA[Developing interactive dashboards for PK simulation data visualization and clinical decision support]]></summary></entry><entry><title type="html">Exposure-Response Analysis: Linking Pharmacokinetics to Clinical Outcomes</title><link href="https://wrlog.github.io/2023/05/20/Exposure-Response-Analysis/" rel="alternate" type="text/html" title="Exposure-Response Analysis: Linking Pharmacokinetics to Clinical Outcomes" /><published>2023-05-20T00:00:00-04:00</published><updated>2023-05-20T00:00:00-04:00</updated><id>https://wrlog.github.io/2023/05/20/Exposure-Response-Analysis</id><content type="html" xml:base="https://wrlog.github.io/2023/05/20/Exposure-Response-Analysis/"><![CDATA[<h2 id="introduction">Introduction</h2>

<p>Exposure-response (E-R) analysis puts numbers on the relationship between drug
exposure metrics and clinical or biomarker endpoints. It’s used for choosing
doses, finding exposure targets, and supporting individualized dosing.</p>

<p>A plain concentration-effect plot can show hysteresis. E-R analysis instead
works with summary exposure metrics (area under the concentration-time curve
(AUC), trough concentration ($C_{min}$), peak concentration ($C_{max}$)) and
relates them to outcomes measured over a suitable time window.</p>

<h2 id="exposure-metrics">Exposure metrics</h2>

<p>AUC is total drug exposure over a dosing interval or treatment period:</p>

\[AUC_{0-\tau} = \int_0^{\tau} C(t) \, dt\]

<p>or at steady state:</p>

\[AUC_{ss} = \frac{Dose}{CL}\]

<p>where $\tau$ is the dosing interval and $CL$ is clearance.</p>

<p>The trough ($C_{min}$) is the concentration at the end of a dosing interval:</p>

\[C_{min} = C_{ss,min} = \frac{Dose}{V} \cdot \frac{e^{-k \cdot \tau}}{1 - e^{-k \cdot \tau}}\]

<p>where $k = CL/V$ is the elimination rate constant.</p>

<p>The peak ($C_{max}$) is the maximum concentration after a dose:</p>

\[C_{max} = C_{ss,max} = \frac{Dose}{V} \cdot \frac{1}{1 - e^{-k \cdot \tau}}\]

<p>For antimicrobials, time above the minimum inhibitory concentration
($T_{&gt;MIC}$) is often used:</p>

\[T_{&gt;MIC} = \int_0^{\tau} \mathbf{1}_{C(t) &gt; MIC} \, dt\]

<h2 id="model-structures">Model structures</h2>

<h3 id="linear">Linear</h3>

<p>The simplest assumes a straight line:</p>

\[E = E_0 + \alpha \cdot Exposure\]

<p>where:</p>
<ul>
  <li>$E$ = response endpoint</li>
  <li>$E_0$ = baseline response</li>
  <li>$\alpha$ = slope parameter</li>
  <li>$Exposure$ = AUC, $C_{min}$, or other metric</li>
</ul>

<h3 id="log-linear">Log-linear</h3>

<p>Often a better fit when exposure spans orders of magnitude:</p>

\[E = E_0 + \beta \cdot \ln(Exposure)\]

<p>or equivalently:</p>

\[E = E_0 + \beta \cdot \ln\left(\frac{Exposure}{Exposure_{ref}}\right)\]

<p>where $Exposure_{ref}$ is a reference exposure value.</p>

<h3 id="emax">Emax</h3>

<p>A sigmoidal curve with a saturating response:</p>

\[E = E_0 + \frac{E_{max} \cdot Exposure^{\gamma}}{EC_{50}^{\gamma} + Exposure^{\gamma}}\]

<p>where:</p>
<ul>
  <li>$E_{max}$ = maximum drug effect</li>
  <li>$EC_{50}$ = exposure producing 50% of maximum effect</li>
  <li>$\gamma$ = Hill coefficient (steepness of curve)</li>
</ul>

<h3 id="logistic">Logistic</h3>

<p>For binary or categorical endpoints:</p>

\[P(Response) = \frac{1}{1 + e^{-(\alpha + \beta \cdot Exposure)}}\]

<p>or with an Emax structure:</p>

\[P(Response) = P_0 + \frac{P_{max} - P_0}{1 + \left(\frac{EC_{50}}{Exposure}\right)^{\gamma}}\]

<h2 id="population-e-r-models">Population E-R models</h2>

<p>Population E-R models include inter-individual variability:</p>

\[E_{ij} = f(Exposure_{ij}, \theta_i) + \epsilon_{ij}\]

<p>where:</p>
<ul>
  <li>$E_{ij}$ = response for individual $i$ at observation $j$</li>
  <li>$\theta_i$ = individual-specific parameters</li>
  <li>$\epsilon_{ij}$ = residual error</li>
</ul>

<p>with individual parameters typically modeled as</p>

\[\theta_i = \theta_{pop} \cdot \exp(\eta_i)\]

<p>where $\eta_i \sim \mathcal{N}(0, \Omega)$ is the inter-individual variability.</p>

<p>Covariates can enter the E-R parameters too, for example:</p>

\[EC_{50,i} = EC_{50,pop} \cdot \left(\frac{WT_i}{70}\right)^{\theta_{WT}} \cdot (1 + \theta_{SEX} \cdot SEX_i)\]

<h2 id="nonmem-implementation">NONMEM implementation</h2>

<h3 id="example-emax-model-with-auc">Example: Emax Model with AUC</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$PROBLEM Exposure-Response Analysis
$INPUT ID TIME AUC DV EVID MDV
$DATA data.csv IGNORE=@
$PRED
E0 = THETA(1)
EMAX = THETA(2)
EC50 = THETA(3) * EXP(ETA(1))   ; without this the fit is naive-pooled
GAMMA = THETA(4)

E_PRED = E0 + (EMAX * AUC**GAMMA) / (EC50**GAMMA + AUC**GAMMA)

Y = E_PRED + EPS(1)

$THETA
(50, 100)   ; E0 baseline
(0, 50)     ; EMAX
(0, 1000)   ; EC50
(0, 1, 5)   ; GAMMA Hill coefficient
$OMEGA
0.09        ; between-subject variability on EC50 (see E_PRED above)
$SIGMA
10          ; residual error variance
$ESTIMATION METHOD=1 INTERACTION MAXEVAL=9999
$COVARIANCE
$TABLE ID AUC E_PRED RES
NOPRINT ONEHEADER FILE=er_model.tab
</code></pre></div></div>

<h3 id="example-logistic-model-for-binary-endpoint">Example: Logistic Model for Binary Endpoint</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$PROBLEM Exposure-Response - Binary Endpoint
$INPUT ID AUC DV
$DATA data.csv IGNORE=@
$PRED
ALPHA = THETA(1)
BETA  = THETA(2)

LOGIT  = ALPHA + BETA * LOG(AUC/100) + ETA(1)
P_RESP = 1 / (1 + EXP(-LOGIT))

; A binary endpoint has no residual error term. F_FLAG=1 tells NONMEM that
; Y is a likelihood rather than a prediction, so the model returns the
; probability of whichever outcome was actually observed.
F_FLAG = 1
IF (DV.EQ.1) Y = P_RESP
IF (DV.EQ.0) Y = 1 - P_RESP

$THETA
(-5, -2, 2)  ; ALPHA intercept
(0, 1, 5)    ; BETA slope
$OMEGA
0.1          ; between-subject variability on the logit
$ESTIMATION METHOD=1 LAPLACE -2LL MAXEVAL=9999
$COVARIANCE
$TABLE ID AUC P_RESP
NOPRINT ONEHEADER FILE=logistic_er.tab
</code></pre></div></div>

<p>There’s no <code class="language-plaintext highlighter-rouge">$SIGMA</code> here. With <code class="language-plaintext highlighter-rouge">F_FLAG=1</code> the model supplies the likelihood
itself, so an additive error on a probability wouldn’t mean anything. The
Laplace method is needed because the first-order conditional approximation
doesn’t apply to a non-continuous likelihood.</p>

<h2 id="finding-target-exposures">Finding target exposures</h2>

<p>For an efficacy endpoint, the target is the exposure that gives the desired
response:</p>

\[Exposure_{target} = \arg\min_{Exposure} |E(Exposure) - E_{desired}|\]

<p>For an Emax model:</p>

\[Exposure_{target} = EC_{50} \cdot
\left(\frac{E_{desired} - E_0}{E_{max} - (E_{desired} - E_0)}\right)^{1/\gamma}\]

<p>The baseline appears in both terms. Writing the denominator as
$E_{max} - E_{desired}$ is a common slip and only works when $E_0 = 0$.</p>

<p>For a safety endpoint, the question is the maximum tolerable exposure:</p>

\[Exposure_{max} = \arg\max_{Exposure} \{Exposure : P(Toxicity) &lt; \alpha\}\]

<p>where $\alpha$ is the acceptable toxicity probability (e.g. 0.05, 0.10).</p>

<p>The therapeutic window is the range between the two:</p>

\[\text{Therapeutic Window} = [Exposure_{eff,min}, Exposure_{safety,max}]\]

<h2 id="uses">Uses</h2>

<p>For dose selection the steps are: find the exposure with the best
efficacy/safety balance, use the PK model to predict the dose that reaches it,
and check that the predicted exposure matches what was observed in clinical
studies.</p>

<p>E-R relationships also support dose adjustment in pediatric patients
(age-dependent exposure differences), renal impairment (reduced clearance),
hepatic impairment (altered metabolism) and drug-drug interactions (predicted
exposure changes). In labeling, E-R analysis is evidence for recommended
regimens, dose adjustment guidelines, therapeutic drug monitoring
recommendations and special population dosing.</p>

<h2 id="model-evaluation">Model evaluation</h2>

<p>Goodness of fit:</p>
<ul>
  <li>Visual: observed vs predicted response, exposure-response scatter plots,
residual plots</li>
  <li>Statistical: objective function value (OFV), AIC/BIC, precision of
parameter estimates</li>
  <li>Predictive: visual predictive checks (VPC), prediction intervals,
cross-validation</li>
</ul>

<p>Sensitivity analyses worth running: compare AUC vs $C_{min}$ vs $C_{max}$ as
the exposure metric, vary the period over which exposure is calculated, and
check how much the timing of the endpoint measurement matters.</p>

<h2 id="other-things-to-consider">Other things to consider</h2>

<p>For chronic treatment, cumulative exposure may be the relevant metric:</p>

\[AUC_{cumulative} = \sum_{i=1}^{n} AUC_i\]

<p>or a time-weighted average:</p>

\[C_{avg} = \frac{AUC_{0-t}}{t}\]

<p>When response lags exposure, add a delay:</p>

\[E(t) = f(Exposure(t - \tau))\]

<p>where $\tau$ is the delay parameter.</p>

<p>If the concentration-effect plot shows hysteresis, the options are an effect
compartment model, an indirect response model, or summary metrics (AUC,
average concentration) that collapse the hysteresis.</p>

<h2 id="where-it-sits-in-the-workflow">Where it sits in the workflow</h2>

<p>E-R analysis usually comes after the PK model:</p>

<ol>
  <li>Develop the population PK model to characterize exposure variability.</li>
  <li>Use it to predict individual exposures (AUC, $C_{min}$, etc.).</li>
  <li>Build the E-R model relating predicted exposures to observed responses.</li>
  <li>Optionally, fit PK and PD jointly.</li>
</ol>

<p>This is the step that connects the PK characterization to efficacy and safety,
and it’s the quantitative basis for picking a dose.</p>]]></content><author><name>Wen Rui Tan</name></author><category term="Pharmacodynamics" /><summary type="html"><![CDATA[Exposure metrics, E-R model structures and NONMEM code for relating drug exposure to clinical endpoints and choosing doses]]></summary></entry></feed>