Skip to content

Repository files navigation

funcml

R-CMD-check CRAN status CRAN checks CRAN downloads License: GPL-3 CRAN Task View: Machine Learning DOI

funcml is a functional machine learning framework for tabular data in R.

funcml is listed in the CRAN Task View: Machine Learning & Statistical Learning.

It provides one explicit interface for the core modeling workflow:

  • fit models with fit()
  • generate predictions with predict()
  • validate performance with evaluate()
  • tune hyperparameters with tune()
  • compare learners with compare_learners()
  • interpret fitted models with interpret()
  • estimate causal effects with estimate()

The package is intentionally compact and opinionated: preprocessing happens before modeling, inputs stay explicit, and the API stays small instead of expanding into a large orchestration layer.

A companion paper for funcml is submitted to JMLR.

Installation

install.packages("funcml")

install.packages("remotes")
remotes::install_github("ielbadisy/funcml")

Core API

The design of funcml centers on a small set of functions:

fit()
predict()
evaluate()
tune()
compare_learners()
interpret()
estimate()

Explore the registry

funcml exposes a session-aware registry of learners, metrics, and interpretation methods.

list_learners()
#>         learner   fit   predict   tune has_fit has_predict has_tune available
#> 17     adaboost fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 24         bart fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 11          C50 fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 20      cforest fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 19        ctree fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 7      densemlp fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 8     e1071_svm fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 13        earth fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 16          fda fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 14          gam fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 10          gbm fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 1           glm fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 3        glmnet fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 12         kknn fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 21          lda fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 23     lightgbm fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 6           mlp fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 15   naivebayes fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 5          nnet fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 18          pls fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 22          qda fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 9  randomForest fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 4        ranger fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 2         rpart fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 26     stacking fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 27 superlearner fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 25      xgboost fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
list_tunable_learners()
#>         learner   fit   predict   tune has_fit has_predict has_tune available
#> 17     adaboost fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 24         bart fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 11          C50 fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 20      cforest fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 19        ctree fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 7      densemlp fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 8     e1071_svm fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 13        earth fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 16          fda fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 14          gam fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 10          gbm fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 1           glm fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 3        glmnet fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 12         kknn fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 21          lda fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 23     lightgbm fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 6           mlp fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 15   naivebayes fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 5          nnet fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 18          pls fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 22          qda fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 9  randomForest fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 4        ranger fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 2         rpart fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 26     stacking fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 27 superlearner fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
#> 25      xgboost fit() predict() tune()    TRUE        TRUE     TRUE      TRUE
list_metrics()
#>               metric direction
#> 1               rmse  minimize
#> 2                mae  minimize
#> 3                mse  minimize
#> 4              medae  minimize
#> 5               mape  minimize
#> 6                rsq  maximize
#> 7           accuracy  maximize
#> 8          precision  maximize
#> 9             recall  maximize
#> 10       specificity  maximize
#> 11                f1  maximize
#> 12 balanced_accuracy  maximize
#> 13           logloss  minimize
#> 14             brier  minimize
#> 15               auc  maximize
#> 16      auc_weighted  maximize
#> 17               ece  minimize
#> 18               mce  minimize
#>                                                       summary     range
#> 1         Root mean squared error for regression predictions.  [0, Inf)
#> 2             Mean absolute error for regression predictions.  [0, Inf)
#> 3              Mean squared error for regression predictions.  [0, Inf)
#> 4           Median absolute error for regression predictions.  [0, Inf)
#> 5  Mean absolute percentage error for regression predictions.  [0, Inf)
#> 6    Coefficient of determination for regression predictions. (-Inf, 1]
#> 7                                    Classification accuracy.    [0, 1]
#> 8                    Macro-averaged classification precision.    [0, 1]
#> 9                       Macro-averaged classification recall.    [0, 1]
#> 10                 Macro-averaged classification specificity.    [0, 1]
#> 11                                   Macro-averaged F1 score.    [0, 1]
#> 12                          Macro-averaged balanced accuracy.    [0, 1]
#> 13  Negative log-likelihood for classification probabilities.  [0, Inf)
#> 14              Brier score for classification probabilities.    [0, 2]
#> 15                                  Area under the ROC curve.    [0, 1]
#> 16              Weighted multiclass area under the ROC curve.    [0, 1]
#> 17      Expected calibration error for binary classification.    [0, 1]
#> 18       Maximum calibration error for binary classification.    [0, 1]
list_interpretability_methods()
#>                                  compute   plot has_compute has_plot
#> 1              interpret(method = "vip") plot()        TRUE     TRUE
#> 2          interpret(method = "permute") plot()        TRUE     TRUE
#> 3              interpret(method = "pdp") plot()        TRUE     TRUE
#> 4              interpret(method = "ice") plot()        TRUE     TRUE
#> 5              interpret(method = "ale") plot()        TRUE     TRUE
#> 6            interpret(method = "local") plot()        TRUE     TRUE
#> 7             interpret(method = "lime") plot()        TRUE     TRUE
#> 8             interpret(method = "shap") plot()        TRUE     TRUE
#> 9      interpret(method = "local_model") plot()        TRUE     TRUE
#> 10     interpret(method = "interaction") plot()        TRUE     TRUE
#> 11       interpret(method = "surrogate") plot()        TRUE     TRUE
#> 12         interpret(method = "profile") plot()        TRUE     TRUE
#> 13 interpret(method = "ceteris_paribus") plot()        TRUE     TRUE
#> 14     interpret(method = "calibration") plot()        TRUE     TRUE

Example data

This README uses funcml::arthritis as the main running example.

Here, status is the outcome for a binary classification task.

demo_dat <- funcml::arthritis
demo_dat$status <- as.factor(demo_dat$status)
levels(demo_dat$status)
#> [1] "No"  "Yes"

Fit a classification model

fit() trains a model and returns a funcml_fit object.

xgb_spec <- list(
  nrounds = 30,
  max_depth = 3,
  eta = 0.1,
  subsample = 1,
  colsample_bytree = 1
)

fit_obj <- fit(
  status ~ age + gender + bmi + diabetes + smoke + covered_health,
  data = demo_dat,
  model = "xgboost",
  spec = xgb_spec,
  seed = 42
)

fit_obj
#> <funcml_fit> classification model: xgboost
#> Formula: status ~ age + gender + bmi + diabetes + smoke + covered_health
#> Features: 6 | Obs: 4856

Generate predictions

The same fitted object can produce class predictions or class probabilities.

predict(fit_obj, demo_dat[1:6, ])
#> [1] Yes No  No  No  No  No 
#> Levels: No Yes
pred_prob <- predict(
  fit_obj,
  demo_dat[1:6, ],
  type = "prob"
)

pred_prob
#>             No        Yes
#> [1,] 0.4382392 0.56176078
#> [2,] 0.5414010 0.45859897
#> [3,] 0.8288925 0.17110750
#> [4,] 0.9638367 0.03616334
#> [5,] 0.5076765 0.49232352
#> [6,] 0.5064105 0.49358952

Evaluate predictive performance

evaluate() applies the same learner under a resampling plan and returns fold-level results with summary statistics.

eval_obj <- evaluate(
  data = demo_dat,
  formula = status ~ age + gender + bmi + diabetes + smoke + covered_health,
  model = "xgboost",
  spec = xgb_spec,
  resampling = cv(v = 4, seed = 42)
)

eval_obj
#> <funcml_eval> model: xgboost | task: classification
#>               metric   mean     sd n std_error conf_level conf_low conf_high
#> 1           accuracy 0.7473 0.0146 4    0.0073       0.95   0.7241    0.7706
#> 2          precision 0.6932 0.0234 4    0.0117       0.95   0.6559    0.7304
#> 3             recall 0.6405 0.0178 4    0.0089       0.95   0.6121    0.6689
#> 4        specificity 0.6405 0.0178 4    0.0089       0.95   0.6121    0.6689
#> 5                 f1 0.6657 0.0195 4    0.0098       0.95   0.6347    0.6968
#> 6  balanced_accuracy 0.6405 0.0178 4    0.0089       0.95   0.6121    0.6689
#> 7            logloss 0.4925 0.0088 4    0.0044       0.95   0.4785    0.5065
#> 8              brier 0.3278 0.0076 4    0.0038       0.95   0.3158    0.3398
#> 9                auc 0.7869 0.0118 4    0.0059       0.95   0.7681    0.8058
#> 10               ece 0.0359 0.0042 4    0.0021       0.95   0.0292    0.0426
#> 11               mce 0.0800 0.0137 4    0.0068       0.95   0.0583    0.1018
plot(eval_obj)

funcml also supports grouped cross-validation, time-based resampling, and holdout validation through group_cv(), time_cv(), and holdout().

Tune hyperparameters

tune() searches candidate hyperparameter settings using the same evaluation framework.

tune_grid <- expand.grid(
  max_depth = c(2, 3),
  eta = c(0.05, 0.1),
  nrounds = c(20, 30)
)

tune_obj <- tune(
  data = demo_dat,
  formula = status ~ age + gender + bmi + diabetes + smoke + covered_health,
  model = "xgboost",
  grid = tune_grid,
  resampling = cv(v = 3, seed = 42),
  metric = "logloss",
  subsample = 1,
  colsample_bytree = 1,
  seed = 42
)

tune_obj
#> <funcml_tune> metric=logloss direction=min search=grid
#> Best:
#>   max_depth eta nrounds   mean     sd n std_error conf_level conf_low conf_high
#> 8         3 0.1      30 0.4933 0.0119 3    0.0069       0.95   0.4636    0.5229
plot(tune_obj)

Compare learners

compare_learners() benchmarks multiple learners under a common resampling design.

compare_obj <- compare_learners(
  data = demo_dat,
  formula = status ~ age + gender + bmi + diabetes + smoke + covered_health,
  models = c("glm", "rpart", "xgboost"),
  metrics = c("accuracy", "logloss"),
  resampling = cv(v = 4, seed = 42),
  specs = list(xgboost = xgb_spec)
)

compare_obj
#> <funcml_compare> task: classification | tuned: FALSE
#>     model   metric   mean     sd n std_error conf_level conf_low conf_high
#> 1     glm accuracy 0.7451 0.0170 4    0.0085       0.95   0.7180    0.7722
#> 2     glm  logloss 0.4897 0.0131 4    0.0066       0.95   0.4688    0.5106
#> 3   rpart accuracy 0.7337 0.0167 4    0.0084       0.95   0.7071    0.7603
#> 4   rpart  logloss 0.5311 0.0130 4    0.0065       0.95   0.5104    0.5517
#> 5 xgboost accuracy 0.7473 0.0146 4    0.0073       0.95   0.7241    0.7706
#> 6 xgboost  logloss 0.4925 0.0088 4    0.0044       0.95   0.4785    0.5065
#>   tuned rank
#> 1 FALSE    2
#> 2 FALSE    1
#> 3 FALSE    3
#> 4 FALSE    3
#> 5 FALSE    1
#> 6 FALSE    2
plot(compare_obj)

Interpret fitted models

interpret() operates directly on fitted funcml_fit objects.

permute_obj <- interpret(
  fit = fit_obj,
  data = demo_dat,
  method = "permute",
  nsim = 20,
  seed = 42
)

summary(permute_obj)
#>          feature   importance      std_dev
#> 1            age 0.0768121911 0.0039101175
#> 2         gender 0.0192030478 0.0032380942
#> 3            bmi 0.0175658979 0.0038212586
#> 4 covered_health 0.0056013180 0.0021176594
#> 5          smoke 0.0053953871 0.0017159311
#> 6       diabetes 0.0005663097 0.0003398597
plot(permute_obj)

A second example shows accumulated local effects for one feature from the same fitted model.

ale_obj <- interpret(
  fit = fit_obj,
  data = demo_dat,
  method = "ale",
  features = c("age"),
  type = "prob"
)

plot(ale_obj)

Partial dependence shows the model-implied effect of one feature, averaged over the rest of the data.

pdp_obj <- interpret(
  fit = fit_obj,
  data = demo_dat,
  method = "pdp",
  features = c("age"),
  type = "prob"
)

plot(pdp_obj)

SHAP values explain an individual prediction. interpret(method = "shap") computes them with funcml’s own Monte Carlo permutation estimate (no external SHAP dependency); plot() supports waterfall, force, beeswarm/summary, importance, dependence, dependence2d, and interaction views.

shap_obj <- interpret(
  fit = fit_obj,
  data = demo_dat,
  method = "shap",
  newdata = demo_dat[1, , drop = FALSE],
  nsim = 30,
  type = "prob",
  seed = 42
)

plot(shap_obj, kind = "waterfall")

The beeswarm/summary view explains the model across many observations at once, ordered by mean absolute SHAP value and colored by feature value.

shap_many_obj <- interpret(
  fit = fit_obj,
  data = demo_dat,
  method = "shap",
  newdata = demo_dat[1:80, , drop = FALSE],
  nsim = 15,
  type = "prob",
  seed = 42
)

plot(shap_many_obj, kind = "beeswarm")

Other supported methods include ICE, local explanations, surrogate models, and interaction diagnostics.

Inspect calibration

For classification, the same interface also supports calibration diagnostics.

calibration_obj <- interpret(
  fit = fit_obj,
  data = demo_dat,
  method = "calibration",
  type = "prob",
  bins = 10,
  strategy = "quantile"
)

plot(calibration_obj)

Decision curve analysis

interpret(method = "dca") reports net benefit across risk thresholds against the “treat all” and “treat none” strategies (Vickers and Elkin, 2006).

dca_obj <- interpret(
  fit = fit_obj,
  data = demo_dat,
  method = "dca"
)

plot(dca_obj)

ROC curve and AUC confidence interval

roc_curve() and auc_ci() are standalone metric functions (backed by pROC) for a full ROC curve and a DeLong or bootstrap confidence interval on AUC.

truth_vec <- demo_dat$status
prob_vec <- predict(fit_obj, demo_dat, type = "prob")[, "Yes"]

roc_obj <- roc_curve(truth_vec, prob_vec)
plot(roc_obj)

auc_ci(truth_vec, prob_vec)
#>      auc conf_low conf_high conf_level method
#> 1 0.8056   0.7929    0.8183       0.95 delong

Estimate causal effects

estimate() extends the same framework to plug-in g-computation estimands such as the ATE.

The example below treats smoke as the treatment variable and status as the outcome, adjusting for the remaining covariates.

est_obj <- estimate(
  data = demo_dat,
  formula = status ~ smoke + diabetes + age + gender + bmi + covered_health,
  model = "glm",
  estimand = "ATE",
  treatment = "smoke",
  interval = "normal",
  seed = 42
)

est_obj
#> <funcml_estimand> ATE via g-computation
#> Treatment: smoke (Yes vs No)
#> Estimate: 0.0897 | SE: 0.0006 | 95% normal CI [0.0886, 0.0908]

By default, plot() shows the model-implied potential outcome distributions under treatment and control, with dashed lines marking the corresponding means. The unit-level effect histogram remains available with plot(est_obj, style = "effects").

plot(est_obj)

The same interface also supports ATT, CATE, and IATE.

Ensembles as first-class learners

Ensembles live in the same learner registry as base models.

stack_fit <- fit(
  status ~ age + gender + bmi + diabetes + smoke + covered_health,
  data = demo_dat,
  model = "superlearner", # or "stacking"
  spec = list(
    learners = c("glm", "rpart", "xgboost", "nnet"),
    learner_specs = list(xgboost = xgb_spec),
    meta_model = "glmnet"
  ),
  seed = 42
)

predict(stack_fit, demo_dat[1:5, ], type = "prob")
#>             No        Yes
#> [1,] 0.3184784 0.68152164
#> [2,] 0.5011472 0.49885283
#> [3,] 0.8758539 0.12414610
#> [4,] 0.9258312 0.07416884
#> [5,] 0.4985016 0.50149842

Summary

funcml provides a compact interface for tabular machine learning in R.

Use it to:

  • train models
  • generate predictions
  • validate performance
  • tune hyperparameters
  • compare learners
  • interpret fitted models
  • estimate causal effects

The package is designed to keep the main analysis workflow explicit.

Contributing

Contributions are welcome.

For development setup, coding standards, and pull request guidelines, see CONTRIBUTING.md.

Citation

If you use funcml in your work, please cite it.

pkgload::unload("funcml")
library(funcml)
citation("funcml")
#> To cite package 'funcml' in publications use:
#> 
#>   El Badisy I (2026). _funcml: Functional Machine Learning Framework_.
#>   R package version 0.8.8, <https://github.com/ielbadisy/funcml>.
#> 
#> A BibTeX entry for LaTeX users is
#> 
#>   @Manual{,
#>     title = {funcml: Functional Machine Learning Framework},
#>     author = {Imad {El Badisy}},
#>     year = {2026},
#>     note = {R package version 0.8.8},
#>     url = {https://github.com/ielbadisy/funcml},
#>   }

About

Functional Machine Learning in R

Topics

Resources

Contributing

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages