Skip to content

fastml

0.7.10

Guarded Resampling Workflows for Leakage-Aware Machine Learning in R

0packages depend
5.9Kdownloads / year
55.0%test coverage
13/13flavors OK

Overview

About

Provides a guarded resampling workflow for training and evaluating machine-learning models. When the guarded resampling path is used, preprocessing and model fitting are re-estimated within each resampling split to reduce leakage risk. Supports multiple resampling schemes, integrates with established engines in the 'tidymodels' ecosystem, and aims to improve evaluation reliability by coordinating preprocessing, fitting, and evaluation within supported workflows. Offers a lightweight AutoML-style workflow by automating model training, resampling, and tuning across multiple algorithms, while keeping evaluation design explicit and user-controlled.

Changes in 0.7.10

Breaking changes

  • **time_varying_cox has been withdrawn.** It required start-stop interval input, which the evaluation path does not accept, so the model could be fitted but never scored. It no longer appears in availableMethods("survival"), and a request for it by name now stops with an error naming the reason and pointing to cox_ph and stratified_cox. Advertising a learner that cannot produce a result is worse than not offering it. Code that named it previously received a fitted model with no metrics.
  • **Requesting resampling or tuning for xgboost_aft is now an error rather than a warning.** parsnip exposes no censored regression mode for xgboost, so the family cannot be fitted through a workflow. Previously the request warned and then fitted natively with resampling silently disabled, which returned a fastml object carrying no fold-level results for a user who had explicitly asked for them. The request is now refused. This is the same contract every other natively fitted survival family already followed.
  • **Values in tune_params are now candidate values, not the endpoints of a range.** A vector of length two was previously read as an interval and interpolated across grid_levels, while a vector of length three or more was read as an explicit set. Nothing but the length distinguished them, so a single call could contain mixture = c(0, 0.5, 1) meaning three values and min_n = c(20, 50) meaning an interval, which is not what the code appears to say. Every value supplied through tune_params is now searched as written, whatever the number of values, and the parameter's range is widened where a requested value falls outside a package default. The package's own default ranges are unchanged and are still interpolated, so results obtained without a tune_params argument are unaffected.
  • **bootstrap_ci now defaults to FALSE.** The interval it produces resamples the held-out predictions of a single fitted model, so it describes uncertainty from the evaluation sample conditional on that fit and excludes training, tuning and model-selection variability. Read as ordinary uncertainty about model performance, which is how a reader of a .lower/.upper column will read it, it overstates precision. Computing it also dominated run time, accounting for about 85% of the elapsed time of a Cox fit to the rotterdam data, which falls from roughly 19 seconds to roughly 2.9 seconds with it disabled, and summary() did not display the result unless show_ci = TRUE was passed, so the cost was paid on every fit for output that was not shown. Set bootstrap_ci = TRUE to restore the previous behaviour. With it off, .lower and .upper are NA and .n_boot is zero, so nothing in the performance table reads as an interval that was not computed. When intervals are displayed, summary() now prints the estimand above the table so the qualification travels with the numbers.
  • **Regression results now report rsq_trad alongside rsq.** yardstick::rsq() is squared Pearson correlation and is invariant to location and scale, so it cannot see a prediction that is biased or mis-dispersed. rsq_trad() is one minus the residual sum of squares over the total sum of squares, which is the quantity most readers assume when they see an R-squared in a results table. The two agree when predictions are unbiased and correctly scaled and diverge when they are not, so the gap between them is a calibration diagnostic. Code that reads the regression metrics table by column position will need updating.

New features

  • **The tuning grid that was actually searched is recorded and printed.** The fitted object gains a tuning_grid component listing, per algorithm and engine, each tuned parameter, the values that were searched on the parameter's natural scale, and whether the parameter came from the user or from the package defaults. summary() prints it under the new type = "grid", which is included in the default type = "all". Reporting only the selected configuration cannot reveal a grid that differs from the one requested, because a selected value looks identical whether the user named it or the package interpolated it, which is how the scale defect below survived. ?fastml documents the scale and the range semantics of tune_params explicitly rather than leaving them to be inferred.
  • **stratified_cox strata are now named through a strata_cols argument.** Strata were previously identified by a naming convention, any column whose name began with strata. A convention that determines model structure but appears nowhere in the function signature is not discoverable, and a user who named a column stratum_site got an unstratified fit with no indication. fastml(..., strata_cols = "site") is now the documented interface; the strata prefix is still recognised, so existing code is unaffected. Naming a column that is not present in the data is an error, and requesting stratified_cox with no strata at all is an error rather than a silent skip.

Bug fixes

  • **Tuning values supplied through tune_params are no longer reinterpreted as logarithms.** dials stores quantitative parameters on their transformed scale, which for penalty, learn_rate, loss_reduction, cost, cost_complexity and rbf_sigma is base-10 logarithmic. User-supplied values were passed to dials::range_set() on the natural scale without being mapped, so learn_rate = c(0.05, 0.1) was fitted as 10^0.05 = 1.122 to 10^0.1 = 1.259, and penalty = c(0.01, 0.1) as 1.023 to 1.259. Nothing in the output reported the substitution. For boosting the consequence is severe, since a learning rate above one overshoots at every step: on an NHANES blood-pressure regression the fitted LightGBM predictions had 1.43 times the standard deviation of the outcome and spanned 36 to 294 mmHg, giving an RMSE of 23.1 against 17.3 for predicting the mean. The reported rsq of 0.19 did not reveal this, because yardstick::rsq() is squared Pearson correlation and is invariant to location and scale; the traditional R-squared was -0.79. Values supplied by the user are now mapped through the parameter's own transform, and a value outside a transformed parameter's domain is refused with an error naming the parameter. Values are now mapped through the parameter's own transform before they reach dials, and a negative value for a positive-domain parameter is refused with an error naming the parameter. A requested zero, which is meaningful for loss_reduction but has no logarithm, maps to the parameter's lower bound and the resulting value is visible in the recorded grid. The same route is taken by the fixed defaults from get_default_params(), which are also written on the natural scale and were also being exponentiated. A user who supplied any tune_params for LightGBM without naming learn_rate was given a learning rate of 10^0.1 = 1.2589 in place of the intended default of 0.1. Only the default *tuning ranges* from get_default_tune_params() are written on the scale dials stores, and those are left alone, so results obtained without a tune_params argument are unaffected. Regression tests are in tests/testthat/test-tuning-grid-scale.R.
Show all changes in 0.7.10Hide the rest of the changes in 0.7.10
  • **Default loss_reduction ranges no longer search implausible values.** The default range was written as c(0, 5) and read as log10, so the searched values were 1, 316 and 100000 rather than the documented [0, 5]. The corresponding entry in the wider registry, c(0, 10), searched up to 1e10. Both are now written on the scale the registry uses and search the documented range. Selection is by cross-validation, so a degenerate grid point was never chosen; the effect was a wasted search budget and a default grid that never explored small values.
  • **Candidate values are no longer dropped when they outnumber grid_levels.** dials::grid_regular() takes at most levels values per parameter, and grid_levels defaults to three, so a user supplying four candidate values silently lost one. Levels are now raised per parameter to the number of values requested, leaving parameters at their defaults untouched so the grid does not grow except where asked.
  • **Default tuning ranges are reachable for survival tasks.** get_default_tune_params() dropped the outcome columns with sym(), which accepts a single symbol, so a survival label naming two or three columns raised "Can't convert a character vector to a symbol" and no default range was ever returned for any survival family. The columns are now dropped by name. Note that penalized_cox has no default range and fits at a fixed penalty of 0.01 with a pure lasso mixture; its penalty is not selected from the data, and a user wanting selection should supply candidates through tune_params or call glmnet::cv.glmnet() directly.
  • **Probability metrics no longer return NA inside a metric set.** yardstick::metric_set() calls its members with na_rm and case_weights, and passes event_level to metrics that accept it. The wrappers for roc_auc, logloss, brier_score and ece did not declare those as formals, so they arrived in ..., where the wrappers read ... as a selection of probability columns. Each metric then returned NA rather than erroring. All four failed together while the six class metrics, whose wrappers already declared these arguments, were unaffected. The four wrappers now declare na_rm, event_level and case_weights, and an event_level supplied by the caller takes precedence over the one captured when the metric was configured. Results obtained without tuning are unchanged, including every figure in the article's case studies, because the affected call path is the one tune_grid() uses.
  • **Nested cross-validation no longer degrades silently when tuning is requested.** Because roc_auc is the default selection metric and probability metrics were empty under tuning (above), select_best() could not choose a configuration. The unfinalized workflow was then fitted anyway, so tune() placeholders reached the engine and the failure surfaced as a message about a hyperparameter, for example "Invalid value for num.trees". Every outer fold failed, the run fell back to ordinary resampling, and the returned object recorded selection$source as "none" with no indication that no nested estimate had been produced. Nested cross-validation consequently worked only when it was not tuning, which is the one case in which it has no purpose. Three changes fix this. Probability metrics compute (above); an outer split whose inner tuning selects nothing is now skipped with a warning naming the selection metric, rather than fitting a workflow that still contains tune() markers; and configuration selection now identifies the configuration by .config and recovers its hyperparameter values from rows that carry them, since tune attaches those columns only to the class-metric rows.
  • **The nested selection rule is documented and reported.** The configuration carried into the returned model is the one with the best mean inner-fold score for the primary metric, pooled across all outer folds, with ties broken by smaller standard error and then a deterministic ordering. It is neither the modal winner across outer folds nor the configuration attached to the best outer score. summary() now prints, alongside a nested estimate, that the figure describes the tuning and fitting procedure rather than the returned model.
  • **A numeric stratification column no longer discards the entire sample.** The stratified_cox path read the stratifier's *values* from the preprocessed frame but its *levels* from the training frame. Any recipe step that rewrites the column's values, step_normalize() being the ordinary case, therefore produced a factor whose levels matched nothing, every row became NA, and coxph() stopped with "No (non-missing) observations". A factor stratifier is untouched by normalisation and so was unaffected, which is why the defect survived hand testing. The stratifier is now read from the training frame, since a grouping label is not a modelled covariate and must not be transformed. If preprocessing changes the number of training rows, the alignment is now refused with an error rather than attempted.
  • **royston_parmar no longer fails when rstpm2 is not attached.** rstpm2::stpm2() resolves an internal helper from the search path, so calling it through the namespace failed with an object-not-found error whenever the user had not attached the package. The fit now runs inside withr::with_package("rstpm2", ...), which attaches the package for the duration of the call and restores the search path afterwards. The attach is a no-op when the package is already attached. This is a side effect on global state, and it is documented as such; the fit is native and unresampled, so it does not execute in a parallel worker.
  • **A survival registry test suite.** tests/testthat/test-survival-registry.R asserts that every family in availableMethods("survival") fits on a canonical right-censored frame under a holdout, and that every combination the documentation records as unavailable raises an error rather than degrading silently. All four defects above were found by hand and would all have been caught by these tests.
  • **XGBoost AFT interval bounds are now supplied on the original time scale (breaking for results, not for the API).** The native xgboost_aft path passed log(time) as label_lower_bound and label_upper_bound. XGBoost's AFT objective models log(Y) = T(x) + sigma * Z and applies the logarithm to those bounds internally, so the previous code applied the transformation twice and optimised the wrong likelihood. The effect was silent on data whose survival times all exceed one, where it degraded the fit, and fatal below one, where the doubly-logged bound is undefined and every risk score came back non-finite. On a simulated dataset with 351 of 400 times below one, the previous code produced 0 of 400 usable risk scores; it now produces 400 of 400 with a concordance of 0.92. Concordance on the lung, rotterdam and flchain benchmarks changes by roughly 0.03, 0.03 and 0.10 respectively. Any previously reported xgboost_aft result should be regenerated. Regression tests pinning the bounds against a direct xgboost reference are added in tests/testthat/test-xgboost-aft-bounds.R.
  • **The redundant seed parameter is no longer passed to xgb.train().** The R xgboost package ignores a seed entry in params and warns about it on every fit, so survival runs emitted a spurious warning. Seeding is and was handled by fastml_with_seed(), which calls set.seed() around the fit, so predictions are unchanged.
  • **base_score is now anchored to the observed time scale for XGBoost AFT.** XGBoost initialises the AFT margin at log(base_score), whose default of 0.5 sits far below survival times measured in days or months. With the logistic AFT loss the objective saturates at that distance, the gradients underflow, no tree splits, and every prediction is identical. The previous log(time) bounds masked this by placing the labels near the default starting point. base_score is now set to the median observed time, which starts the optimiser on the scale of the data. Without it, concordance on lung and rotterdam collapses to exactly 0.5.
  • **Grouped and time-ordered holdout constraints are now enforced together.** Supplying both block_col and group_cols previously warned that grouping would be ignored and cut the holdout purely by time, so a group could contribute rows to both the training and the test set — the exact contamination group_cols is meant to prevent. The holdout is now cut at the admissible point nearest the requested test_size, where a point is admissible only if no group spans it, so every training row precedes every test row *and* no group appears on both sides. Because such a point need not fall at the requested proportion, the realized test fraction is reported through a warning when it differs by more than five percentage points. When groups are interleaved in time no admissible point exists, and fastml() now stops with an explanatory error instead of silently dropping a guarantee. Fold construction under blocked_cv and `

The NEWS section is longer than shown here.

From NEWS.md in the release

Install

Health

CRAN checks
13OK
Slowest check: 6.9 min · r-oldrel-windows-x86_64
Status changes
  • OK13 OK2026-08-05
  • NOTE12 OK, 1 NOTE2026-08-01
  • OK13 OK2026-06-09
  • ERROR12 OK, 1 ERROR2026-06-08
  • OK13 OK2026-05-02
Show 2 earlier changesHide 2 earlier changes
  • ERROR11 OK, 1 ERROR2026-04-25
  • OK14 OK2026-03-10
Advisories

none known

Documentation

In release 0.7.10
READMEYes · 940 wordsVignettesNoNEWSYes · 100% structured · mentions 0.7.10Citation fileNoWebsiteYes · pkgdown
Help pages
74 (26 internal)
Examples that run
18%2 run in a plain check · 3 only when donttest examples are enabled (R CMD check --as-cran) · 6 never run
Exported functions with examples
20%
Documented parameters
98%
Return-value docs
93%
Help pages citing references
0%

Downloads

5.9K
CRAN downloads in the past year
Rank #5,597 · ~16/day · ~492/mo
Daily download trend is not available in this view yet.
52630 days
1.4K90 days
5.9K1 year
Compare downloads with other packages →
Also on142 r2u11 autocran

Repository

111Stars
8Forks
10Open issues
0Open PRs
5Releases
553Commits
3Commit identities
553 commits · Last activity 2026-09-29 · +1.8% stars, 30d
Responsiveness
How these are sampled
PRs merged (all time)
98%
Median days to close, 50 most recently updated closed issues
7
Median days to close, 50 most recently updated closed PRs
0

Stars over time

2025-09-08 · 1002026-09-29 · 111
PracticesBeta

9 development-tooling and community-health practices detected across 7 families in the upstream repository

Checks run against github.com/selcukorkmaz/fastml on 2026-10-11.

Continuous integration (2)
GitHub ActionsR CMD check workflow

The R CMD check workflow runs on Linux, macOS and Windows, and on R-devel

Governance and community (2)
Code of conductContributing guide
Docs source (1)
pkgdown
Coverage (1)
Coverage reporting
Show all practicesHide 3 practices
Research and citation (1)
JOSS paper
Lint, format, editor (1)
RStudio project
Git structural (1)
.gitattributes
How this is detected·Detection ruleset v3 (2026-09-26)
AI coding toolsBeta

This repository shows signs of AI coding tools.

  • Codexfirst seen 2025-06-05
    Pull request it wrote: opened by a project member
  • Claude Codefirst seen 2026-01-19
    Seen in the repository: .claude
    Named in .Rbuildignore: .claude
    Named in .gitignore: .claude
    Commits: at least 3 crediting it in the message
Models named in its commits
  • Claude Opus 53 commits · Sep 2026
Counts are commits, not lines of code. Commits by a tool's accounts count once across all of its accounts. Commits crediting it are the largest count among its credit lines, a minimum.How this is detected·Detection ruleset v4 (updated 2026-09-27)

Dependencies

Reverse dependencies
0direct
0indirect

Nothing depends on this yet.

People & History

People (3)
Author, Maintainer since 0.1.0 (2024)
Author · added in 0.4.0
Releases (15)

Pick two releases to compare their code metrics.

  • 0.7.10Latest
    record
    2026-08-28 · current release · diff ↗
  • 0.7.9
    record
    2026-08-19 · diff ↗
  • R
    R 4.6.0 released · 2026-04-24
  • 0.7.8
    record
    2026-03-17 · diff ↗ ·
    added 1 export
    save_fastml
  • 0.7.7
    record
    2026-01-27 · diff ↗ ·
    added 16 exports
    compare_defaults, estimate_tuning_time, explain_stability, get_default_differences, get_default_params_with_warnings, get_expanded_tune_params, get_parsnip_default_engine, get_parsnip_default_params, get_tuning_complexity, get_tuning_params_for_complexity, print_default_differences, print_tuning_presets, recommend_tuning_config, reset_default_warnings, validate_defaults_registry, warn_default_override
  • 0.7.6
    record
    2026-01-10 · diff ↗
  • 0.7.5
    record
    2025-12-22 · diff ↗ ·
    removed 1 export
    evaluate_models
    added 2 exports
    fastml_compute_holdout_results, predict_model.model_fit
  • 0.7.0
    record
    2025-10-29 · diff ↗ ·
    added 9 exports
    counterfactual_explain, explain_ale, explain_dalex, explain_lime, interaction_strength, plot_ice, predict_risk, predict_survival, surrogate_tree
  • 0.6.2
    record
    2025-07-24 · diff ↗
  • 0.6.1
    record
    2025-06-11 · diff ↗
  • 0.6.0
    record
    2025-06-10 · diff ↗ ·
    removed 1 export
    save_model
    added 1 export
    save.fastml
  • R
    R 4.5.0 released · 2025-04-11
  • 0.5.0
    record
    2025-03-07 · diff ↗ ·
    added 11 exports
    availableMethods, flatten_and_rename_models, get_best_model_idx, get_best_model_names, get_best_workflows, get_default_engine, get_default_params, get_default_tune_params, get_engine_names, get_model_engine_names, process_model
  • 0.4.0
    record
    2025-01-08 · diff ↗ ·
    removed 1 export
    explain
    added 3 exports
    fastexplain, fastexplore, sanitize
  • 0.3.0
    record
    2024-12-16 · diff ↗ ·
    added 1 export
    explain
  • 0.2.0
    record
    2024-12-01 · diff ↗ ·
    removed 1 export
    validate_tuneGrid
Show 2 earlier eventsHide 2 earlier events
  • 0.1.0
    record
    2024-11-12
  • R
    R 4.4.0 released · 2024-04-24

Package metadata

First published
2024-11-12
Total releases
15 / 2 yrs
License
MIT + file LICENSE OSI · relicensed from GPL (>= 2) in 0.6.0
Minimum R
≥ 4.1.0
Download size
387 KB

Cite

Cite this package

Run in R for the authors' preferred citation:

citation("fastml")
Korkmaz, S., Goksuluk, D., & Karaismailoglu, E. (2026). fastml: Guarded Resampling Workflows for Leakage-Aware Machine Learning in R (Version 0.7.10) [Computer software]. https://doi.org/10.32614/CRAN.package.fastml

This package has no citation file, so citation() returns this entry built from DESCRIPTION.

Cite the R Observatory

For a number measured here: a download total, a coverage figure, an archival date.

APA

Balamuta, J. J. (2026). R Observatory: Metrics for fastml version 0.7.10 [Data set]. HJJB, LLC. Data release v2026-10-17. https://doi.org/10.5281/zenodo.21843040

From data release v2026-10-17, which the citation names so these numbers can be found later. More on citing and the projects behind them.

Report a problem with this page →

Privacy choices

These apply to this browser and are stored on this device only. Nothing about your choice is sent to us.

Read the privacy policy