Added automatic model diagrams (#10). modelGraph()
parses a model’s differential equations (from a model function,
rxode2 model/UI or an nlmixr2 fit) into a
graph of compartments and flows (mass transfer, eliminations, inputs and
non-mass-transfer interactions like effect compartments or PD
stimulation/inhibition), detecting dosing compartments from the dosing
records. modelDiagram() lays it out with dosing and
absorption compartments above the central compartment, peripheral
compartments to the left, eliminations/metabolites below and PD models
to the right, and draws it with the "DiagrammeR"
(Graphviz), "ggplot2" or "dot" (DOT source)
engine. Dosing properties (lag(), f(),
rate(), dur()) are shown as annotations on
their compartment, and delay() terms are understood. The
diagrams were checked against all 3043 models of ‘nlmixr2lib’, including
large QSP/PBPK models. plot() of an rxode2
user interface (rxUi) object or compiled
rxode2 model draws its diagram, and the new “Automatic
model diagrams” vignette describes the feature.
Fixed vpcPlot() with method = "tidyvpc"
when the observed data has observation records with a missing
DV. Those records were dropped from the observed data but
kept in the simulation, so a stratified (e.g. multiple-endpoint) VPC
errored and a single-endpoint VPC paired simulated values with the wrong
observations. The matching simulated records are now dropped too
(#74).
plot() of a multiple-endpoint fit no longer creates
empty “Endpoint:” groups for state compartments without observations
(like depot or central); only observed
endpoints are plotted. plot() of an augPred()
object likewise skips endpoints without data (#44).
vpcPlot()/vpcPlotTad() of a
multiple-endpoint fit whose data codes the endpoints with
cmt no longer shows an extra NA panel (or a
single NA panel with pred_corr = TRUE); the
simulated compartment labels are now matched to the observed ones by
name (#44).
The censored VPC (vpcCens(), or
vpcPlot() with cens = TRUE and the
vpc backend) of a multiple-endpoint fit now stratifies by
its endpoints only, matching the simulated endpoints to the observed
ones; an endpoint whose observations are all missing no longer becomes
an NA panel (#44).
plot() of a censored multiple-endpoint fit no longer
errors in geom_cens() (“argument must be coercible to
non-negative integer”) for an endpoint without censored observations
(#44).
The censored VPC (vpcCens(),
vpcCensTad(), or vpcPlot() with
cens = TRUE and the vpc backend) now honours
the data argument; it previously always used the fitted
data, whatever data was supplied (#55). The observed data
now comes from the original (or supplied) dataset instead of the fit, so
censored records are identified from the CENS column rather
than from the fit’s imputed DV (which, for
censMethod = "cdf", could land above the limit and be
counted as uncensored). Records without an observation are dropped, as
in the fit.
The VPC observed data now drops records with MDV = 1
even when an EVID column is also present (previously only
EVID was checked when both existed).
plot() of an augPred() object now
accepts a base-R style log argument
(log = "y", "x" or "xy") to draw
the individual plots on log-scaled axes; non-positive values, which
cannot be shown on a log axis, are dropped (#32).
vpcPlot(), vpcPlotTad(),
vpcCens() and vpcCensTad() now use a supplied
nlmixr2est::vpcSim() simulation instead of discarding it
and re-simulating with the default n = 300. Supplying
n alongside a simulation warns that it is ignored, and
pred_corr = TRUE errors unless the simulation was created
with pred = TRUE (#57). The observed-data prediction
correction for a supplied simulation is computed from that simulation’s
own fit rather than from whichever vpcSim(pred = TRUE) ran
last.
Fixed the VPC simulation ignoring the data argument
of vpcPlot(), vpcPlotTad(),
vpcCens() and vpcCensTad(); the simulation
always used the fit’s dataset, so it did not match the observed data
built from data (and tidyvpc warned that
xsim did not match the observed x-values). The simulation
now uses data when supplied. A supplied
nlmixr2est::vpcSim() simulation was made from its own
dataset, so passing data alongside one now warns that it
only replaces the observed data (#68). Because the simulation now
follows data, an endpoint without observations in
data is no longer simulated either, so it drops out of a
multiple-endpoint VPC instead of being shown as a simulated-only panel.
For a model with a non-normal endpoint, data now needs a
numeric CMT column (the model’s compartment numbers), since
the compartments would otherwise be taken from the fitted data by row
number, or read from a factor’s level order, and would not match
data.
Fixed the prediction-corrected VPC
(pred_corr = TRUE) ignoring the data argument;
the observed data was rebuilt from the fit’s dataset instead of the
supplied data (#62).
Fixed stratified censored VPCs
(vpcCens()/vpcCensTad(), or
vpcPlot() with cens = TRUE) failing with “The
following specified stratification columns were NOT found in observation
data”; the observed data now comes from the original (or supplied)
dataset, which keeps the stratification columns that the fit table drops
(#56, #55).
Fixed the confidence-band width (and its legend label) for
vpcPlot()/ vpcPlotTad() with the
tidyvpc backend; ci = c(lower, upper) was
passed to tidyvpc::vpcstats() as
conf.level = ci[2] instead of the actual interval width
ci[2] - ci[1], so the default
ci = c(0.05, 0.95) (a 90% CI) was drawn and labeled as a
95% CI.
Fixed vpcPlotTad()/vpcCensTad() (and
any idv taken from the fit) with a user-supplied
data that is a subset or reordering of the fitted data;
tad was merged from the fit by row number, so it silently
became NA (or was paired with the wrong row). Each supplied
row is now matched by content to the fitted row it came from, and a
warning is given for observations that are not in the fitted data
(#60).
vpcCensTad() (and
vpcCens()/vpcPlot()/vpcPlotTad()
with cens = TRUE and a non-time idv); the
censored VPC now passes the sim/obs column
mappings to vpc::vpc_cens() explicitly instead of relying
on column guessing (nlmixr2#390).dv is now mapped to the sim column instead of
being copied into a new dv column, so vpc
groups the simulated data by replicate as it does for the uncensored
VPC.quantile() “missing values and NaNs not
allowed” error in prediction-corrected
vpcPlot()/vpcPlotTad() on censored (LLOQ/ULOQ)
fits with the vpc backend; censored records are now dropped
before the pred-corrected VPC (which is shown for non-censored data
only), matching the vpc package’s stated behavior
(nlmixr2#390).vpcPlot()/vpcPlotTad() with
method = "tidyvpc", cens = TRUE, which previously errored
that the observed data had no cens column; the censoring
column is now retained when the observed dataset is rebuilt for
pred-correction.plot() on a fit with between-subject variability (BSV)
now adds a nested "bsv" section (inside each
data/compartment group) with QQ plots for each BSV parameter, BSV-BSV
correlation plots (when more than one BSV parameter is present) and, via
the new covariate argument, BSV-by-covariate plots
(box-and-whisker for categorical or low-cardinality covariates, scatter
plus a linear trend with confidence interval for continuous ones).
Covariate values are taken from each subject’s first record, so they are
assumed time-invariant (#51).plot() (for fits and
augPred objects) are now gglist objects from
the ggtibble package instead of the internal
nlmixr2PlotList class. They still print/plot all figures at
once and support broadcasting ggplot2 additions
(e.g. plot(fit) + ggplot2::theme_bw()), and the top-level
object remains a named, nested collection
(plot(fit)[["Endpoint: ..."]]). The collection can also be
converted to a ggtibble for reporting with
ggtibble::as_ggtibble().augPred plots are now paginated with
ggforce::facet_wrap_paginate(), removing the hand-written
16-IDs-per-page chunking. Requires
ggtibble (>= 1.0.3.9000) and ggforce; the
minimum R version is now 4.3.plot() on a fit (and on an augPred object)
is now about 3x faster. The paginated individual plots were expanded
one-per-page with ggtibble::as_gglist(), which deep-copies
the plot through serialize()/unserialize();
because those plots are built inside functions that hold the whole fit,
each copy walked the entire fit object. The pages are now produced by
re-adding the paginated facet for each page instead.rxode2::.linCmtSensB() test guards.
That internal was removed from rxode2 in 2025, so the
try()/skip_if_not() checks always fell through
and never skipped. linCmt() gradients are now always
available, so the guards are unnecessary and the tests run
unconditionally.tidyvpc
packageOps method (removing
+ method) to work with ggplot2 version 4 (#39)+vpcPlot() where input data frame for models
did not stratify all the time when the columns were upper case.plot() now returns a named list of lists so that
users can more easily choose which plots to include, if all plots are
not desired. Or the user could use those names as the basis of figure
captions (fix #8).
Models without eta values (between subject variability) now have more consistent plotting to models with eta values (fix #18).
The package was updated to align with changes in ggplot2.
The package was updated to skip tests with "focei"
and linCmt()
Added fixes for upcoming ‘ggplot2’ 3.4 release
Added changes for the generalized log-likelihood supported by ‘nlmixr2est’
Added a NEWS.md file to track changes to the
package.