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.
install.packages("funcml")
install.packages("remotes")
remotes::install_github("ielbadisy/funcml")The design of funcml centers on a small set of functions:
fit()
predict()
evaluate()
tune()
compare_learners()
interpret()
estimate()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 TRUElist_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 TRUElist_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 TRUEThis 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() 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: 4856The 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 Yespred_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.49358952evaluate() 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.1018plot(eval_obj)funcml also supports grouped cross-validation, time-based resampling,
and holdout validation through group_cv(), time_cv(), and
holdout().
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.5229plot(tune_obj)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 2plot(compare_obj)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.0003398597plot(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.
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)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_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 delongestimate() 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 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.50149842funcml 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.
Contributions are welcome.
For development setup, coding standards, and pull request guidelines,
see CONTRIBUTING.md.
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},
#> }










