GUI upload fixes:
Large uploads are now accepted. Shiny's default request-size limit of 5 MB
rejected normal inputs such as an 8 MB adjusted model. The GUI now raises the
limit to 500 MB, so any model or data file a user is likely to upload is
accepted (shiny.maxRequestSize, set in fastret_app()).
Excel uploads are read robustly. Instead of always reading the first
worksheet, FastRet now searches every worksheet for the first one that
contains the required columns (RT/SMILES/NAME for training, selective
measuring and adjustment; NAME/SMILES for prediction) and silently drops
any extra columns. Previously, uploading a workbook with additional columns
besides the required ones - or with the data on a sheet other than the first -
could cause model training to stall. New internal helpers read_input_xlsx()
and drop_unsupported_columns() implement this and are covered by tests.
Chemical-descriptor cache:
The chemical-descriptor cache is now a pure on-disk SQLite database
(CDs.sqlite) instead of an in-memory R option backed by CDs.rds. On first
use the shipped database is copied into the per-user cache directory
(tools::R_user_dir("FastRet", "cache")) and all reads and writes go there.
Because the cache lives on disk, descriptors computed in one process are
immediately visible to every other process and worker, so parallel training
and the GUI's background workers now share a single cache. Concurrent access
is made safe via SQLite WAL mode and INSERT OR IGNORE.
getCDs(), preprocess_data(), train_frm(), predict.frm() and
adjust_frm() gained a cache argument. With cache = TRUE (default)
descriptors are read from and written to the on-disk cache; with
cache = FALSE the cache is bypassed and every descriptor is recomputed via
rCDK (useful for benchmarking the uncached runtime).
Removed the internal FastRet.cachedCDs option and the N_SMI_CACHED
constant. CDK / rcdklibs >= 2.9 is required (already enforced at GUI startup
by check_cdk_version()), as it provides all descriptors in CDFeatures.
GUI:
Reworked the model-adjustment controls in the Adjust tab. The old
"Components of linear model" checkbox group (which let users toggle the
RT, RT^2, RT^3, log(RT), exp(RT), and sqrt(RT) predictors of a
linear adjustment model) has been replaced by an "Adjustment method" radio
button that lets users pick the model family used for the correction:
Lasso (recommended), Linear model, or XGBoost. Lasso and XGBoost adjust
using the base retention time together with the molecular descriptors and can
therefore capture compound-specific shifts, whereas the linear model fits a
simple straight-line correction from the base retention time only. The GUI now
passes adj_type to adjust_frm() accordingly; the deprecated RT-transformation
predictors are no longer exposed.
Promoted the four workflows to dedicated navbar tabs (Train, Select, Adjust, Predict) instead of selecting them from a single drop-down. Each tab shows a short label with the full mode description on hover, and carries only its own controls and results. The Privacy Policy, Contact, and About pages remain as separate tabs.
Simplified the Train sidebar to the essentials: data upload, method
(XGBoost/Lasso), seed, and the console log. The "Show advanced settings"
toggle and the "Preprocessing Options" checkbox group were removed --
GUI training now always uses the package defaults of train_frm().
GUI-trained models are now reproducible by default. The training seed
previously defaulted to the current system time and was never actually passed
to train_frm(); it now defaults to a fixed value (42) and is applied, so
training the same data twice yields identical models. The seed remains
user-editable.
The console log is now always visible in every mode (the per-mode "Show console logs" checkboxes were removed), including a new console-log box for Selective Measuring.
The Selective Measuring tab now exposes the selection variant (named as in the paper: SMmax, SM1, SM0, SMinf, with an info button explaining each) and the random seed, so users can pick the variant and reproduce a selection from the GUI.
Reviewed the GUI help texts for clarity, tone and formatting, and reworded the prediction console output so it no longer implies that descriptors are recomputed when they are served from the cache.
Each tab's sidebar now starts with a heading (the mode name) and a help button that explains, in a few sentences, what the mode is for and when to use it.
Testing:
shinytest2 end-to-end GUI tests (tests/testthat/test-gui-e2e.R)
that drive the real app via headless Chrome and exercise every mode and model
type (Train: Lasso + XGBoost; Selective Measuring; Predict; Adjust: Lasso +
Linear model + XGBoost). They are skipped on CRAN and when Chrome/Java are
unavailable.Documentation:
misc/scripts/make-gui-screenshots.R.Bugfix:
toscutil::stub() development call from the cross-validation
path of train_frm(). The call had no effect on results (it only assigned
predict.frm argument defaults into a function environment that was never read
again), but it made toscutil — a Suggests package — a hard runtime dependency.
As a result, model training failed with "there is no package called 'toscutil'"
whenever Suggests packages were not installed (e.g. a plain CRAN install).Dependencies:
future (>= 1.40.0). start_gui() uses
with(future::plan(...), local = TRUE), which relies on the
with.FutureStrategyList method introduced in future 1.40.0.Documentation:
GUI-Usage vignette, and fixed minor wording in the GUI help texts.Bugfix:
predict.frm() now imputes missing/non-finite values not only for base model
predictors, but also for adjustment model predictors. This prevents NA
predictions if the adjustment model depends on predictors that are
missing/non-finite in the new data.Internal Improvements:
predict.frm() into a shared internal
helper.Internal Improvements:
start_gui() and start_gui_in_devmode() now rely on
with(future::plan(...), local = TRUE) so that temporary future plans are
restored automatically without manual bookkeeping.API Improvements:
selective_measuring() accepts "max" and "inf" as additional values
for its rt_coef parameter:
"max" is an alias for the existing "max_ridge_coef""inf" sets all chemical descriptor features to zero before clustering so that
RT alone drives the distance metric (i.e., it is "infinitely" more important
than the chemical descriptors).Bugfix:
adjust_frm() automatically switched to "lm" adjustment if predictors
contained only one predictor, regardless of whether add_cds was TRUE or
FALSE. This has now been fixed, so that "lasso", "ridge" and "gbtree"
adjustment is now possible if either add_cds is TRUE, add_cds is
NULL or predictors contains more than one predictor.API Improvements:
match_rts and match_keys to adjust_frm():
match_rts=TRUE (default), RTs are obtained by matching rows in
new_data to rows in frm$df based on match_keys.match_rts=FALSE, RTs are obtained by applying the base model to
new_data.match_keys can be any combination of "INCHIKEY", "SMILES" and "NAME". If
left at default NULL, SMILES+INCHIKEY is used if both columns are present
in the adjusted and the original training data. Otherwise, SMILES+NAME is
used.API Improvements:
getCDs():
plot_frm():
preprocess_data():
add_cds to control whether chemical descriptors should be
added to the input data using getCDs().rm_ucs to control whether unsupported columns (i.e.
columns that are neither mandatory nor optional) should be removed from the
input data.rt_terms to control whether transformations of the RT column
(square, cube, log, exp, sqrt) should be added to the input data.CDFeatures are allowed as
optional columns.train_frm():
do_cv to control whether cross-validation should be
performed for performance estimation. Default is TRUE.method now accepts two values for training models with xgbtree
base: "gbtreeDefault" (train xgboost with default params) and "gbtreeRP"
(train xgboost with parameters optimized for the RP dataset). The old value
"gbtree" still works and is now an alias for "gbtreeDefault".frm objects are fully
specified now).clip_predictions(). Of course, the clipping is
always based on the RT range of training folds, not the whole original
training data.predict.frm():
degree_polynomial>1
and/or interaction_terms=TRUE, unless the transformations were manually
applied to the new data beforehand.clip to allow clipping of predictions to be within
the RT range of the training data. Works for both adjusted and unadjusted
models.clip=FALSE. See clip_predictions()
for details.impute=FALSE in predict.frm().selective_measuring():
rt_coef, allowing user to control the influence of RT on
the clustering. A value of 0 means that RT is ignored, a value of
"max_ridge_coefficient" means that RT has the same weight as the most
important chemical descriptor and a value of 1 means no scaling at all
(except standardization to z-scores, which is applied before to the whole
dataset before the ridge regression is trained).adjust_frm():
seed to allow reproducible results.do_cv to control whether cross-validation should be
performed for performance estimation. Default is TRUE.adj_type to control which model should be trained for
adjustment: supported options are "lm", "lasso", "ridge", or "gbtree".
Previously, only "lm" was supported. To stay backwards compatible, the default
is "lm".add_cds to control whether chemical descriptors should be
added to the input data using getCDs(). Only recommended for adj_type other
than "lm".clip_predictions(). Of course, the clipping is
always based on the RT range of training folds, not the whole original
training data.print.frm():
clip_predictions():
train_frm(), predict.frm() and
adjust_frm().get_predictors():
base and adjust to control whether predictors for the
base model, the adjustment model or both should be returned.Bugfixes:
preprocess_data() are now generated
correctly as product of the involved features instead of a division. This
follows common practice in regression modeling and avoids division by zero
issues. Passing older models, trained with division-based interaction terms,
to downstream functions like predict.frm() or adjust_frm() will now lead
to an error. (This is not a breaking change, as predict.frm() and friends
have in fact never been able to handle such models).plot_frm() with type "scatter.cv.adj" or "scatter.train.adj" now correctly
shows retention times from the new data (used for model adjustment) as x-axis
values instead of the original training retention times.catf() now only emits escape codes (i.e. colored output), it the output is
directed to a terminal. If the output is redirected to a file or a pipe, no
escape codes are emitted anymore. Since catf() is used throughout the
package for logging, this fixes the output for the whole package.Internal Improvements:
adjust_frm()fit_gbtree()fit_glmnet()get_param_grid()get_predictors()getCDs()plot_frm()predict_frm()preprocess_data()selective_measuring()train_frm()validate_inputdata()caret dependency by adding custom implementations for:
createFolds()nearZeroVar()adjust_frm() into a private function
merge_dfs().fit_glmnet(), fit_lasso() and fit_ridge() with a single
function fit_glmnet(), that takes the method ("lasso" or "ridge") as
parameter. Instead of a dataframe df that has to contain only predictors
plus the RT column (as response), the function now takes a matrix of
predictors X and a vector of responses y. This makes the function more
flexible and easier to test.fit_gbtree_grid() with a much simpler function
find_params_best(). Instead of allowing the specification of every grid
parameter, the new function instead accepts a keyword searchspace for
specifying predefined grids to choose from.fit_gbtree by exposing lots of hardcoded internal xgboost
parameters as function parameters with sensible defaults. In particular, the
user can now set xpar to "default", "rpopt" or a predefined grid-size to
train the model with different hyperparameter settings. Furthermore, the
function is now written in a way that works with both, version 1.7.9.1 and
the new 3.1.2.1 version published on 2025/12/03 (yes, version 2.x was skipped
completely).get_param_grid() for returning predefined
hyperparameter grids for xgboost model training based on keywords like
"tiny", "small" or "large".benchmark_find_params() to benchmark runtime of
find_params_best() for different numbers of cores and/or threads. As it
turns out, choosing a higher number of cores is usually more efficient (at
the cost of worse progress output).named(), as_str(), is_valid_smiles() and
as_canonical()selective_measuring() by aligning glmnet coefficients to columns by
name (more stable) and by including RT, scaled by max(abs(coefs)), in PAM
clustering.libwebp-dev as dependency to Dockerfile.Measurements_v8.xlsx to inst/extdata/. The new
list contains corrections to the old RP dataset plus 1660 new measurements
measured on a total of 18 different chromatographic environments.seed parameter to selective_measuring() function for reproducible
clustering resultstrain_frm() functiondigest and shinybusy dependenciesinst/mockdata/getCDsFor1Molecule(), get_cache_dir(), ram_cache (these
were exported, but declared as internal)parLapply2Improved read_retip_hilic_data():
the dataset is now only downloaded from GitHub if the package is not installed.
If it is installed, the dataset is loaded directly.
Internal Changes:
TODOS.mdutil.R to data.Rmisc/datasetsload_all() and document() to util.Rxlsx and readxl packages with openxlsxAdded a cache cleanup handler that gets registered via
reg.finalizer() upon package loading to ensure that the cache
directory is removed if it doesn't contain any files that should
persist between R sessions.
Added an article about installation details incl. a troubleshooting section
Improved function docs
Improved examples by removing donttest blocks
Improved examples & tests by using smaller example datasets to reduce runtime
Moved patch.R from the R folder to misc/scripts, which is
excluded from the package build using .Rbuildignore. The file
is conditionally sourced by the private function
start_gui_in_devmode() if available, allowing its use during
development without including it in the package.
Added \value tags to the mentioned .Rd files describing the
functions' return values.
Added Bonini et al. (2020) doi:10.1021/acs.analchem.9b05765
as reference to the description part of the DESCRIPTION file,
listing it as Related work. This reference is used in the
documentation for read_retip_hilic_data() and ram_cache. No
additional references are used in the package documentation.
Added Fadi Fadil as a contributor. Fadi measured the example datasets shipped with FastRet.
Added ORCID IDs for contributors as described in [CRAN's checklist for submissions].
read_rp_xlsx() and read_rpadj_xlsx()
into donttest to prevent note "Examples with CPU time > 2.5
times elapsed time: ...". By now I'm pretty sure the culprit is
the xlsx package, which uses a java process for reading the
file. Maybe we should switch to openxlsx or readxl in the
future.preprocess_data() to prevent note
"Examples with CPU time > 2.5 times elapsed time:
preprocess_data (CPU=2.772, elapsed=0.788)".getCDs()Added examples to start_gui(), fastret_app(), getCDsFor1Molecule(),
analyzeCDNames(), check_lm_suitabilitym(), plot_lm_suitability(),
extendedTask(), selective_measuring(), train_frm(), adjust_frm(),
get_predictors()
Improved lots of existing examples
Added additional logging messages at various places
Submitted to CRAN, but rejected because the following examples caused at least one of the following notes on the CRAN testing machines: (1) "CPU time > 5s", (2) "CPU time > 2.5 times elapsed time". In this context, "CPU time" is calculated as the sum of the measured "user" and "system" times.
| function | user | system | elapsed | ratio | | -------------------- | ------| ------ | ------- | ----- | | check_lm_suitability | 5.667 | 0.248 | 2.211 | 2.675 | | predict.frm | 2.477 | 0.112 | 0.763 | 3.393 | | getCDs | 2.745 | 0.089 | 0.961 | 2.949 |
Completely refactored source code, e.g.:
Added a test suite covering all important functions
The UI now uses Extended Tasks for background processing, allowing GUI usage by multiple users at the same time
The clustering now uses Partitioning Around Medoids (PAM) instead of k-means, which is faster and much better suited for our use case
The training of the Lasso and/or XGBoost models is no longer
done using caret but using glmnet and xgboost directly.
The new implementation is much faster and allows for full
control over the number of workers started.
Function getCDs now caches the results on Disk, making the
retrieval of chemical descriptors much faster
The GUI now has a console element, showing the progress of the background tasks like clustering and model training
The GUI has a cleaner interface, because lots of the options are now hidden in the "Advanced" tab by default and are only displayed upon user request
Initial version.
Copy of commit cd243aa82a56df405df8060b84535633cf06b692 of Christian
Amesöders Repository.
(Christian wrote this initial version of FastRet as part of his master thesis
at the Institute of functional Genomics, University of Regensburg).
This is the first CRAN release since 1.3.0.
It includes all changes listed below for versions 1.3.1 to 1.5.1, notably the 1.3.7 fix: cross-validation in train_frm() no longer requires the suggested package toscutil, so model training no longer fails in a plain CRAN install without Suggests packages.
Changes in this version:
Replaced a structure(.Names = ) call in the GUI server code with stats::setNames(), fixing the R-devel CRAN check NOTE about deprecated special names.
The xgboost grid search (tune_grid) now passes the training objective inside params, as xgboost 3.x requires; xgboost had warned that the old form will become an error in a future version.
Added an inst/CITATION file and a reference to the FastRet paper (Fadil et al., 2026, doi:10.1021/acs.jcim.6c01344) in the package description and README.
Fixed the privacy-contact link in the GUI, which pointed to a page of the University of Regensburg that no longer exists.
Fixed a typo in the biocViews field of DESCRIPTION.
README: installation now leads with the CRAN release, mentions the hosted GUI at https://fastret.spang-lab.de and lists Selective Measuring as a feature.
Vignettes: corrected installation instructions, broken anchors, outdated function names and typos; documented the hyperparameter-tuning checkbox (Train) and the compound-matching option (Adjust) of the GUI; extended the contribution guidelines.
CI: updated GitHub Actions to their current major versions.
updateCachedCDs()) and the example-data
generator (misc/scripts/make-example-data.R) now download
Measurements_v10P.xlsx from the version-independent example-data release tag
instead of the v1.3.0 package release, so all example/source artifacts live in
one place and the build no longer pins an old package-version tag.Example datasets & models refresh:
The bundled RP dataset (and inst/extdata/RP.xlsx) was regenerated from the
published Measurements_v10P.xlsx and now contains 458 metabolites (was
442), with an added INCHIKEY column.
inst/extdata/RP_adj.xlsx (returned by read_rpadj_xlsx()) is now real
data: 25 RP metabolites re-measured under a steeper gradient (dataset
RP_Steep in the FastRet paper), replacing the previous synthetic file. It
also gains an INCHIKEY column.
inst/extdata/RP_lasso_model.rds was retrained on the new 458-metabolite RP.
The shipped chemical-descriptor cache (inst/cachedata/CDs.sqlite) was rebuilt
from Measurements_v10P.xlsx; the outdated inst/extdata/Measurements_v8.xlsx
is no longer shipped.
Documentation and the Shiny GUI now offer direct download links to the example files (hosted as GitHub release assets), so web-app users can follow the tutorials without installing the package.
Model adjustment (compound matching):
The default compound matching used by adjust_frm() now matches on
"INCHIKEY" alone (when complete InChIKeys are available in both new_data
and frm$df), instead of the previous c("SMILES", "INCHIKEY"). The InChIKey
is a structure-invariant identifier and alone uniquely identifies a molecule,
whereas SMILES is matched as an exact raw string and often differs in spelling
between datasets — so including it only made matching more fragile without
adding identifying power. This also aligns the package default with the
InChIKey-based matching used throughout the FastRet paper's adjustment
benchmarks. When InChIKeys are unavailable the fallback is unchanged
(c("SMILES", "NAME")), and an explicit match_keys argument still overrides
the default.
The GUI's Adjust tab gained a "Compound matching" dropdown that exposes this
choice: Automatic (recommended; InChIKey when available, else Name+SMILES),
InChIKey, Name, or Name + SMILES. The selection is passed to
adjust_frm() as match_keys.
Hyperparameter tuning:
train_frm()'s tune_grid argument now also accepts the grid-size keywords
"tiny", "small" and "large" (in addition to an explicit data.frame),
e.g. train_frm(method = "brt", tune_grid = "small").
The built-in "small" grid was changed to a fast, effective 8-combination
Retip-style grid (max_depth 2-5, eta 0.01/0.02, with gamma = 1,
colsample_bytree/subsample 0.5, min_child_weight 10; the number of
boosting rounds is chosen by early stopping). In the FastRet paper's benchmark
this internal grid search matches the accuracy of Retip's own grid search.
(The predefined grids were not previously exposed, so this is not a breaking
change.)
The GUI's Train tab gained a "Tune hyperparameters (grid search)" checkbox. When enabled for XGBoost it runs the small grid search (a few extra seconds); it has no effect for Lasso.
Bug fixes (GUI):
Training from an uploaded workbook that contained an INCHIKEY column
crashed the whole GUI session (the UI greyed out and had to be restarted).
train_frm() keeps INCHIKEY in frm$df as metadata, but the Train tab's
result handler excluded only NAME/RT/SMILES before calling
round(cds, 2), so the character INCHIKEY column made round() error. As
this ran inside a reactive status observer without a tryCatch, the error
tore down the session. The handler now excludes INCHIKEY too, and the
extended-task status observers are wrapped so any future handler error shows a
notification instead of killing the session. (This was not fixed by the 1.4.1
robust-upload change, which deliberately keeps INCHIKEY.)
Dropped upload columns are no longer silent. When an uploaded workbook
contains columns FastRet does not use (anything other than
NAME/RT/SMILES/INCHIKEY/RT_ADJ and chemical descriptors), the GUI now
shows a notification naming the ignored columns, so it is clear they are not
carried into the results/download.
train_frm() gains a tune_grid argument: supplying a data.frame of
xgboost hyperparameters runs a cross-validated grid search over those rows for
gbtree/BRT models (instead of the fixed default parameters), making FastRet's
existing grid-search machinery user-accessible. NULL (default) is unchanged.