Every machine learning package in R has its own API and its own
output format. tidylearn puts one signature over 20 of them:
tl_model() picks the underlying package from the
method you name, and whatever comes back is a tibble or a
ggplot2 object.
The algorithms are untouched. glmnet, randomForest, xgboost, e1071,
cluster and dbscan do the fitting, and model$fit hands you
the object they returned — model$fit$model for an
unsupervised method, whose $fit is the list of tidied
components — so nothing here closes off the package underneath.
This vignette covers the shape of a workflow end to end. The articles listed at the bottom go deeper on each step.
The core of tidylearn is the tl_model() function, which
dispatches to the appropriate underlying package based on the method you
specify. The wrapped packages include stats, glmnet, randomForest,
xgboost, gbm, e1071, nnet, rpart, cluster, and dbscan.
Logistic regression handles two-class problems, so we take a binary
subset of iris here. For three or more classes use "tree",
"forest", "svm" or "nn".
# versicolor and virginica overlap, so this is a real classification
# problem -- setosa is linearly separable from the other two, which makes
# logistic regression fail to converge
iris_binary <- iris %>%
filter(Species %in% c("versicolor", "virginica")) %>%
mutate(Species = droplevels(Species))
model_logistic <- tl_model(iris_binary, Species ~ ., method = "logistic")
print(model_logistic)
#> tidylearn Model
#> ===============
#> Paradigm: supervised
#> Method: logistic
#> Task: Classification
#> Formula: Species ~ .
#>
#> Training observations: 100Predictions come back as a tibble with a .pred column.
What .pred contains depends on type:
"class" gives the predicted label, "prob"
gives one column per class.
# Predicted class labels
predictions <- predict(model_logistic, type = "class")
head(predictions)
#> # A tibble: 6 × 1
#> .pred
#> <fct>
#> 1 versicolor
#> 2 versicolor
#> 3 versicolor
#> 4 versicolor
#> 5 versicolor
#> 6 versicolor# Class probabilities
head(predict(model_logistic, type = "prob"))
#> # A tibble: 6 × 2
#> versicolor virginica
#> <dbl> <dbl>
#> 1 1.000 0.0000117
#> 2 1.000 0.0000486
#> 3 0.999 0.00120
#> 4 1.000 0.0000422
#> 5 0.999 0.00141
#> 6 1.000 0.000102Note that the default type = "response" means different
things across methods — probabilities for logistic regression, class
labels for trees and forests. Ask for type = "class"
explicitly when you want labels, or let tl_evaluate()
handle it:
# Principal Component Analysis
model_pca <- tl_model(iris[, 1:4], method = "pca")
print(model_pca)
#> tidylearn Model
#> ===============
#> Paradigm: unsupervised
#> Method: pca
#> Technique: pca
#>
#> Training observations: 150# Transform data
transformed <- predict(model_pca)
head(transformed)
#> # A tibble: 6 × 5
#> .obs_id PC1 PC2 PC3 PC4
#> <chr> <dbl> <dbl> <dbl> <dbl>
#> 1 1 -2.26 -0.478 0.127 0.0241
#> 2 2 -2.07 0.672 0.234 0.103
#> 3 3 -2.36 0.341 -0.0441 0.0283
#> 4 4 -2.29 0.595 -0.0910 -0.0657
#> 5 5 -2.38 -0.645 -0.0157 -0.0358
#> 6 6 -2.07 -1.48 -0.0269 0.00659# K-means clustering
model_kmeans <- tl_model(iris[, 1:4], method = "kmeans", k = 3)
print(model_kmeans)
#> tidylearn Model
#> ===============
#> Paradigm: unsupervised
#> Method: kmeans
#> Technique: kmeans
#>
#> Training observations: 150tl_prepare_data() handles imputation, scaling and
encoding in one call, and records what it did so the same transformation
can be replayed on new data:
# Simple random split
split <- tl_split(iris, prop = 0.7, seed = 123)
# Train model (three species, so a multiclass-capable method)
model_train <- tl_model(split$train, Species ~ ., method = "forest")
# Test predictions
predictions_test <- predict(model_train, new_data = split$test)
head(predictions_test)
#> # A tibble: 6 × 1
#> .pred
#> <fct>
#> 1 setosa
#> 2 setosa
#> 3 setosa
#> 4 setosa
#> 5 setosa
#> 6 setosa# Stratified split (maintains class proportions)
split_strat <- tl_split(iris, prop = 0.7, stratify = "Species", seed = 123)
# Check proportions are maintained
prop.table(table(split_strat$train$Species))
#>
#> setosa versicolor virginica
#> 0.3333333 0.3333333 0.3333333
prop.table(table(split_strat$test$Species))
#>
#> setosa versicolor virginica
#> 0.3333333 0.3333333 0.3333333
prop.table(table(iris$Species))
#>
#> setosa versicolor virginica
#> 0.3333333 0.3333333 0.3333333tidylearn provides a unified interface to these established R packages:
| Method | Underlying Package | Function Called |
|---|---|---|
"linear" |
stats | lm() |
"polynomial" |
stats | lm() with poly() |
"logistic" |
stats | glm(..., family = binomial) |
"ridge", "lasso",
"elastic_net" |
glmnet | glmnet() |
"tree" |
rpart | rpart() |
"forest" |
randomForest | randomForest() |
"boost" |
gbm | gbm() |
"xgboost" |
xgboost | xgb.train() |
"svm" |
e1071 | svm() |
"nn" |
nnet | nnet() |
"deep" |
keras | keras_model_sequential() |
| Method | Underlying Package | Function Called |
|---|---|---|
"pca" |
stats | prcomp() |
"mds" |
stats, MASS, smacof | cmdscale(), isoMDS(), etc. |
"kmeans" |
stats | kmeans() |
"pam" |
cluster | pam() |
"clara" |
cluster | clara() |
"hclust" |
stats | hclust() |
"dbscan" |
dbscan | dbscan() |
The raw model from the underlying package is reachable through
$fit:
# Example: Access the raw randomForest object
model_forest <- tl_model(iris, Species ~ ., method = "forest")
class(model_forest$fit) # This is the randomForest object
#> [1] "randomForest.formula" "randomForest"
# Use package-specific functions if needed
# randomForest::varImpPlot(model_forest$fit) # nolintAn unsupervised method returns tidied components as well, so its
$fit is the list holding them and the wrapped object sits
at $fit$model:
Split, fit, predict, score — the four steps this vignette covered, in the order they run:
# Quick example combining everything
data_split <- tl_split(iris, prop = 0.7, stratify = "Species", seed = 42)
# Random forests are scale-invariant, so no scaling is needed here. When a
# method does need scaled inputs, the same transformation has to be applied
# to the test set -- see the Supervised Learning vignette.
model_final <- tl_model(data_split$train, Species ~ ., method = "forest")
test_preds <- predict(model_final, new_data = data_split$test)
accuracy <- mean(test_preds$.pred == data_split$test$Species)
cat("Test accuracy:", round(accuracy * 100, 1), "%\n")
#> Test accuracy: 95.6 %vignette("data-ingestion") — reading from files,
databases and cloud sourcesvignette("supervised-learning") — classification and
regression in depth, and how to replay preprocessing on a test setvignette("unsupervised-learning") — clustering,
ordination, and choosing the number of clustersvignette("market-basket") — association rulesvignette("tuning-and-pipelines") — hyperparameter
search, and bundling a workflow you can savevignette("automl") — searching across methods under a
time budgetvignette("diagnostics") — assumptions, influence and
model comparisonvignette("reporting") — plots and formatted
gt tablesvignette("integration-workflows") — combining
supervised and unsupervised stepsvignette("compute-backends") — when a fit is too slow
or too large for this machine: GPU routing, cost estimates, and the
cloud safety model