The figures from bootplot() (also shown by plot() of a bootstrapped
fit), plot() of a multistart() result and linearizePlot() no longer
carry the fit, the multistart or linearized object, the full plotting
data or the frame they were called from in their environments, so saving
them (with saveRDS() or as a 'targets' target) is far smaller. Each figure now stores its data once, in
$data; the plots look the same.
bootplot() returns the figure when 'ggtext' is not installed; it returned
only the plot labels.
New multistart() re-estimates a model from many perturbed starting
points, so a fit that settled in a local optimum can be recognized. It
takes either a fit or a model plus data, works with any estimation
method, and returns a nlmixr2Multistart object holding every start's
objective function and parameter estimates alongside the best fit.
plot() on the result gives the objective-function waterfall
(type = "waterfall", the default) and the parameter-stability plot
(type = "parameters").
Starting points are drawn around the initial estimates by "uniform"
(the default), "lhs" (Latin hypercube) or "normal" sampling,
respecting fixed parameters and declared bounds. By default the
candidates are pre-screened with a cheap empirical-Bayes objective
evaluation so that only the most promising ones are fully estimated,
and each start is cached to disk so that an interrupted run resumes
where it left off. See multistartControl() for the options.
covarSearchAuto() selects unit-scale covariates again. A candidate
coefficient is added at exactly 0; 'nlmixr2est' (>= 7.0.2) nudges such a
parameter to foceiControl(zeroTheta) (0.001) and FOCEi then steps it by
that amount, so a numeric covariate on a unit scale (e.g. a z-score) never
left zero and was never selected. Each new coefficient now starts at
0.1/max(|covariate|), a step matched to the covariate's units that keeps
the starting effect within 0.1 for every subject.
linearize() no longer diverges when re-estimating a model whose residual
error parameters are small. In the linearized model those parameters are
ordinary thetas, so FOCEi scaled them as linear parameters (1/|estimate|)
rather than as residual errors (0.5*|estimate|); for a combined1() model
this drove add.sd into its lower bound and left the objective function
33 points above where the refit started. They now keep their residual
scaling, which also converges in far fewer iterations.
preconditionFit() accepts a decorated covariance method. nlmixr2est
reports the sandwich as "|r|,|s|" when a matrix needed the absolute-value
correction (or "r+,s+" when it was nudged positive-definite), but the retry
loop compared against the bare "r,s", so a good result read as a failure.
It then re-preconditioned the already-preconditioned fit until the R matrix
was numerically singular and solve() gave up with "system is
computationally singular". A singular preconditioning matrix is now also
reported as a preconditioning failure naming the try, rather than as a bare
solve() error (#128).
The same applies to the " (full)" scope suffix nlmixr2est appends when the
installed covariance spans theta + residual sigma + Omega rather than the
structural-theta block alone (foceiControl(covFull=), TRUE by default), so
"r,s (full)" is recognized as the sandwich too. The shape does not matter
to preconditionFit(): the preconditioner is widened to whatever parameter
space the returned covariance spans.
preconditionFit() works again. It built the reparameterized model lines
through symengine, which cannot parse an identifier containing a ., so a
conventional residual name like add.sd (as nlmixr2Pre_add.sd) raised
"SymEngine exception: Parse error" and made the function unusable for most
models. The lines are now assembled directly from the preconditioning
matrix, which also drops the symengine dependency from this path (#124).
preconditionFit() no longer fails with "non-conformable arguments" on a
model with random effects. fit$R spans only the population parameters
while the fit covariance also carries the omega elements, so the
preconditioner is now widened to the covariance's own parameter space --
identity off the theta block -- which keeps the theta/omega cross-covariances
correct.
linearize() works on a model with a correlated eta block. The generated
model was built by pasting each entry of ui$eta into a model line, but for a
correlated block that property also carries the off-diagonal entry -- e.g.
(eta.cl,eta.v) -- which produced mu_(eta.cl,eta.v) = ... and failed to
parse. The eta names are now taken from the diagonal of the ini data frame
(#126).
Regenerate the stored theoFitOde fit. It was built against an older
'nlmixr2est', and its saved $control no longer matched what the
current estimator expects, so anything that re-ran the model through
that control -- bootstrapFit(), profile(), or a plain
nlmixr2(fit$finalUiEnv, ..., control = fit$control) -- failed with
"attempt access index 130/129 in VECTOR_ELT".
addorremoveCovariate() no longer turns the iniDf neta1/neta2
columns into character (#110). The row it adds set them to
NA_character_, and rbind() promotes the whole column to match, so
max() and order() on those columns became lexicographic further
downstream -- with ten or more etas max() returned "9" rather than
10, so the next eta index collided with an existing one.
Ini rows that are built by hand (adding a covariate in
addorremoveCovariate(), adding thetas during linearization) no longer
hard-code their column list, so they still rbind() with an iniDf that
carries the prior column newer versions of lotri add for prior
distributions (#109). Both shapes of the data frame are handled, so this
works with lotri versions that have the column and versions that do not.
getMinAICFit()
returns the fit with the lowest AIC, listModelsTested() builds a
Description/AIC/dAIC table ready for pander::pander(), and
isBoundaryFit() reports whether a fit has a parameter at its
boundary. By default both selection helpers exclude boundary fits.
See the new "reporting helpers" article.Fix bootstrapFit(stratVar=), which did not actually resample. The
stratified branch called sample(list(uids), ...), and since
list(uids) has length one every draw returned the whole vector of
subject ids, so the bootstrap datasets did not depend on the seed
(#99). Three further problems in the same code are fixed with it:
the new subject ids restarted at 1 in each stratum, so subjects from
different strata were merged under a shared id; the sample was split
across strata by the number of observations rather than the number
of subjects, over-weighting strata whose subjects have more
records; and rounding each stratum up could return more subjects than
nSampIndiv asked for.
A stratified bootstrap now always draws whole subjects. When
stratVar changed within a subject, that subject's records were
split between strata and resampled as separate (partial) subjects;
each subject is now stratified by its first value, with a warning.
nlmixr2extra:::sampling() now resolves its uid_colname default
before using it. Called without one it sampled ncol(data) subjects
instead of the number of subjects in the data. It also accepts a
tibble, which previously produced a one column tibble where a vector
of subject ids was expected.
Fix covarSearchAuto() crashing with "wrong arguments for subsetting
an environment" when a covariate is selected; the best model is now
re-fit to recover its fit object. Also corrected the forward
inclusion test, which had an inverted sign so improving covariates
were never selected (#103)
bootstrapFit() now works for models with a single estimated
population parameter, a single random effect, or no random effects at
all. Previously the bootstrap summary collapsed 1-row / 1x1 quantile
arrays to vectors (and could not summarize a NULL omega), causing
bootstrapFit() to error with dim(X) must have a positive length,
incorrect number of dimensions, or 'data' must be of a vector type, was 'NULL'. Printing the bootstrap summary of a model with no random
effects no longer errors either.
optimUnisampling() now keeps N and floorT when it retries
internally. Before, the recursive call reset them to the defaults, so
asking for a sample size other than 1000, or for un-floored values,
could silently return 1000 integer samples instead (#97)
The bundled theoFitOde fit was regenerated and can now be read
without the qs2 package. Its origData and parHistData had been
serialized with qs2, so without that package installed those slots
could not be decoded and fit$dataMergeInner() -- and anything built
on it, such as the nlmixr2rpt figures -- failed.
Add focei/foce linearization
Add formula interface
Add vignettes on linearization, formula interface, log-likelihood profiling and preconditioning.
profile() method for likelihood profiling (Issue #1)bootstrapFit() fixes se option (Issue #66)bootstrapFit() now will be more careful handling NA values so
they do not completely affect results (Issue #59)
bootstrapFit() will now only take the correlation of the non-zero
diagonals (Issue #59).
New method for knit_print() will generate model equations for LaTeX
reporting automatically.
Tests are now skipped if they contain linear compartment models that need gradients when the gradients are not compiled (as in the case of intel c++).
Use assignInMyNamespace() instead of using the global assignment
operator for the horseshoe prior
Be specific in version requirements (as requested by CRAN checks)
Move the theoFitOde.rda data build to devtools::document() to
reduce CRAN build time (could add more standard models like warfarin
for package developers which takes way too much time for CRAN)
Fix cli issues with the new cli 3.4+ release that will allow
bootstrapping to run again (before cli would error, this fixes the
donttest issues on CRAN).
Fixed step-wise covariate selection to work a bit better with the updated UI, thanks to Vishal Sarsani
Added lasso covariate selection (thanks to Vishal Sarsani)
Added horseshoe prior covarite selecion (thanks to Vishal Sarsani)
Added a NEWS.md file to track changes to the package.