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()predict()evaluate()tune()compare()interpret()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()
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() benchmarks multiple learners under a common
resampling design.
compare_obj <- compare(
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:
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.9, <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.9},
#> url = {https://github.com/ielbadisy/funcml},
#> }