saveFit() no longer zips and deletes the files of another fit whose name
extends its own with -<suffix> (#10). It picked a fit's files out of the
target directory by the name pattern <file>-*, so with the loose files of
saveFit(fit2, "fit-alt", zip=FALSE) in the directory, saveFit(fit) put
them in fit.zip and then deleted them. The same match also swept in the
files an earlier zip=FALSE save of the same name left behind, so an item
the new fit lacks (e.g. origData with data=FALSE) came back from the old
fit on load. saveFit() now writes every file into a private temporary
directory and zips (or, with zip=FALSE, copies) only those, so other
files already in the target directory are never read, zipped or removed.
A save still replaces the fit saved earlier under the same name: a
zip=TRUE save removes that fit's loose loader <file>.R, and a
zip=FALSE save removes its <file>.zip, which loadFit() would
otherwise load instead -- each only when it really is a saved fit, so an
unrelated script or archive of that name is kept. A target directory that
is an existing file is now an error rather than being written over.
A := cache under a nlmixr2save.prefix no longer overwrites (and, on
loading, can no longer lose) an unrelated <name>.zip in the cache
directory while writing or reading <prefix><name>.zip, and a prefix that
names a directory (e.g. "run1/") now creates it instead of silently
failing to write the cache.
loadFit() now loads a fit given as a path, with or without the .zip (or
.R) extension: loadFit("path/to/fit.zip") and loadFit("path/to/fit")
both work from any working directory. It used to append .zip to whatever
it was given, so a path ending in .zip looked for fit.zip.zip and failed
with "cannot find fit file" even though the file existed. It also extracted
the archive into the working directory rather than beside the .zip, so a
fit in another directory was never found, and a same-named file already in
the working directory was overwritten and then deleted. The archive is now
extracted to a temporary directory instead.
loadFit() also loads a fit that an earlier version saved under a path --
saveFit(fit, "models/fit"), saveFit(fit, "/home/me/models/fit") or
saveFit(fit, "~/models/fit") -- from anywhere, including on another
user's machine. Those versions wrote the path into the loader script
inside the archive: it named the fit after the path and read every file
from it, so loading elsewhere failed with "cannot open file" or
"Permission denied" for the original location. A ~ path also garbled
the names the items were restored under. The archive is now extracted
flat, and a loader tied to a path is regenerated from the files in it; the
restored fit matches one saved without a path. A .zip renamed after
saving loads too.
loadFit(myfit) with a bare symbol loads myfit.zip again when no object
myfit exists; it looked for a file named after the "object not found"
error instead.
saveFit(fit, "path_model/fit") now writes the files inside path_model/
under the bare name fit, creating the directory if needed. The files, and
the loader's references to them, used to be named path_model/fit-..., so
the archive stored a path_model/ folder: unzipping it (including by
loadFit(), which unzipped into the working directory) recreated
path_model/ wherever that happened, and the loader only worked from the
directory it was saved from.
New vignette, "Keeping fits in a models directory", on sending := caches
to a directory with nlmixr2save.dir (and naming them with
nlmixr2save.prefix), setting that for a whole project, saving and loading
by path, and committing the directory to version control.
loadFit() no longer rebuilds a fit's models while loading. A saved fit
stores its compiled model lists (foceiModel, saemModel) and its ui as
rxode2::rxode2() calls, and all of them were rebuilt on load -- for a
large model, several long C compilations that looked like a hang, plus a
parse of the whole model for the ui. Each is now built only when
something first uses it: estimates, tables, fixef(), summary() and the
like need none of them; print() and augPred() build the ui, and
re-estimation or residual recalculation compiles the model list.
Re-saving a loaded fit (e.g. nlmixr2saveShare()) writes the model lists
back without compiling them. Fits saved under a path by an earlier version
get this too, since their loader is regenerated.
loadFit() (and therefore :=) no longer depends on the installed lotri
to read a cache. A fit's matrices (cov, omega, R, phiC, ...) are
stored as lotri({...}) blocks with one row per statement; loadFit() now
reads that form itself and passes anything else to lotri. Some development
versions of lotri rejected the named rows saveFit() writes ("matrix
expression should be 'name ~ c(lower-tri)'"), which is what broke the
:= example on the package website, and a cache could not be loaded at all
without nlmixr2est attached, since the scripts call lotri() unqualified.
Existing caches benefit without being re-saved.
loadFit() now brings a restored iniDf0 in line with the installed
rxode2's iniDf. It takes the columns and types from the fit's own ui,
which the installed rxode2 rebuilds on load, rather than from a version
check. A cache written before rxode2 added prior gains the column (as
NA), and an all-NA prior -- which reads back from the .csv as
logical -- is character again, as rxode2 keeps it. The reloaded fit's
iniDf0 now matches the original.
fit := nlmixr2(...) no longer fails when data.table is attached after
nlmixr2save (e.g. library(nlmixr2); library(data.table)). data.table's
exported := masked nlmixr2save's and errored with "Check that
is.data.table(DT) == TRUE". nlmixr2save now re-attaches itself in front
of data.table when data.table is attached; data.table's DT[, a := b] is
unaffected (#8).
saveFit() now restores every iniDf0 column with the type it had when
saved, rather than a fixed list of columns. rxode2's newer character
prior column is all NA for fits without priors, which read.csv() read
back as logical, so a loaded fit's iniDf0 no longer matched the original.
loadFit() (and therefore :=) now restores the fit table's ID column as a
factor. The table round-trips through a plain .csv, so ID came back as an
integer while a live fit carries a factor; anything joining the fit table to
something derived from the fit then hit a type mismatch, since
nlme::augPred() keeps its id a factor. ggPMX::pmx_nlmixr() on a cached
fit failed outright with "Incompatible join types: x.ID (factor) and i.ID
(integer)". The repair happens on load, so caches written by earlier versions
are fixed too.
saveFit() now records the parHistData$type factor levels for fits that
store parHistData compressed (the nlmixr2est default). It read the levels
straight out of the fit environment, where a compressed fit keeps a raw
vector rather than a data frame, so the levels were silently not recorded and
loadFit() fell back to a hardcoded level list. nlmixr2est has since added
types that list predates ("Analytic Gradient (relaxed)" and friends), and
those came back as NA.
loadFit() now repairs a parHistData$type that the cache's own restore
script dropped to NA. Those levels are applied by the script stored inside
the cache, so a cache written before nlmixr2est added a type has no level for
it and coerces it to NA -- and re-saving cannot recover it, because by then
the string is already gone. loadFit() reads the column back from the
-parHistData.csv in the cache and appends whatever the script was missing,
so existing caches are repaired in place.
Saving or loading a fit no longer disturbs another cache whose base name it
is a suffix of. The list of files belonging to a fit was matched with an
unanchored pattern, so a cache named fit also matched myfit-env.R --
zipping another cache's files into its own archive and then deleting them
from disk. This is reachable whenever unzipped files are lying around, which
saveFit(zip=FALSE) leaves by design. The names are now matched literally
rather than as a regexp, so a base name holding a metacharacter (my.fit is
an ordinary R name, and as a pattern its . also matched my_fit) is
matched exactly.
saveFit(fit, zip=FALSE) now actually leaves the fit unzipped for a fit table
(a nlmixr2FitData). The method wrote the fit .csv and then called the
core method with a hardcoded zip=TRUE, so the argument was silently ignored
for every fit that carries data.
nlmixrDataSimplify() gained est and control arguments and no longer
drops the covariate columns that est="vae" searches for. The VAE covariate
search picks its covariates out of the data instead of out of the model, so
the candidate columns (as identified by nlmixr2est::vaeCovariates(), matched
to the vaeControl() search options) are now kept in the simplified data and
included in the hash. When the installed nlmixr2est does not export
vaeCovariates() the search is skipped rather than erroring.
You can now export a fit without the original data so a fitted model can be shared when the subject-level data cannot:
saveFit(fit, data = FALSE) (or options(nlmixr2save.data = FALSE))
writes a fit whose origData is omitted from the zip.nlmixr2saveShare() strips an existing saved fit (a live object or a
.zip base name; honors nlmixr2save.dir/nlmixr2save.prefix) into a
shareable sibling zip: fit-noData.zip (data removed) or, with
noFit = TRUE, fit-noData-noFit.zip (data and the returned
prediction/residual table removed, keeping the model, estimates, eta table
and parameter history). The original fit is left unchanged.See vignette("sharing-fits"), which also documents the side effects (e.g.
VPC, residual re-derivation, and re-fitting need the original data).
saveFit() no longer stores the redundant model element (the loader always
rebuilds it from ui). This removes a spurious "could not determine how to
save object of class call for item model" warning when re-saving a
previously loaded fit (including via nlmixr2saveShare()).
Saved fits now record the nlmixr2est and rxode2 versions they were
produced with (including each package's commit sha when it was installed from
a remote such as GitHub). When a fit is later loaded and the installed
nlmixr2est or rxode2 differs:
loadFit() warns that the fit was run with a different package version
(controllable with the new checkVersion argument).:= caching operator, in an interactive session, asks whether to
rerun the fit with the currently installed packages; non-interactively it
loads the cached fit and warns. (Trusted-cache mode,
nlmixr2save.check = FALSE, is left untouched so committed caches stay
stable across versions.)Fits saved by older nlmixr2save versions (which carry no version metadata)
continue to load without any warning. The whole check can be turned off with
options(nlmixr2save.checkVersion = FALSE) (or per call via
loadFit(..., checkVersion = FALSE)); it is TRUE by default. See
vignette("version-tracking").
The parHistData$type factor levels are now recorded from the fit at save
time and restored on load, so the factor round-trips correctly regardless of
which nlmixr2est version produced it (the level set has grown over versions,
e.g. "Analytic Gradient"). A hardcoded fallback covers fits saved before
this was recorded.
The := caching operator gains three options() (mirroring
nlmixr2save.quiet):
nlmixr2save.prefix (default ""): prepended to the assigned variable
name to form the cache file, e.g. with options(nlmixr2save.prefix =
"modelPiping-"), fit := nlmixr2(...) caches to modelPiping-fit.zip.nlmixr2save.dir (default "."): the directory the cache files live in.nlmixr2save.check (default TRUE): when TRUE, := keeps verifying
the cache against the current model/data/arguments (historical behavior);
when FALSE, := simply loads the cache file if it exists (a fit as
.zip, a simulation/other value as .rds) and otherwise runs and saves
it -- the cache is trusted and regenerated only when missing. This keeps
a committed cache stable across nlmixr2/rxode2 versions.nlmixr2saveInvalidate(): deletes every := cache entry under the active
nlmixr2save.prefix/nlmixr2save.dir, so cached fits/simulations are re-run
on the next render when nlmixr2save.check is FALSE.The saved-fit loader script now quotes its variable name, so a
nlmixr2save.prefix containing non-syntactic characters (e.g. -) round-trips
correctly.
Support both $parFixedDf structures produced by nlmixr2est: the
current one (named "Estimate"/"SE" columns) and the upcoming
$parFixed refactor (nlmixr2est#645, unnamed columns). The
structure of the fit is detected when it is saved and recorded in the
restore script, so fits round-trip exactly with either version
(nlmixr2saveParFixedDf() gained a named argument; existing saved
zip files continue to load unchanged).
Additional all-NA numeric columns of $parFixedDf (for example
"CI Lower"/"CI Upper" when there is no covariance step) are now
coerced back to numeric when a saved fit is loaded, instead of being
left as logical columns from the CSV round-trip.
Create zip files that are (mostly) R independent to save nlmixr2 fit items
Create a cached assignment operator := to save nlmixr2 fits
automatically; if used, it will load a cached fit if function
arguments are the same (a sort of disk memoization of the outermost function)
Initial CRAN submission.
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.