# Getting Started with tidylearn

## Introduction

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.

## Installation

    # From CRAN
    install.packages("tidylearn")

    # Development version
    # devtools::install_github("ces0491/tidylearn") # nolint

    library(tidylearn)
    library(dplyr)

## The Unified Interface

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.

### Supervised Learning

#### Classification

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: 100

Predictions 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.000102

Note 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:

    tl_evaluate(model_logistic, metrics = c("accuracy", "f1"))
    #> # A tibble: 2 × 2
    #>   metric   value
    #>   <chr>    <dbl>
    #> 1 accuracy  0.98
    #> 2 f1        0.98

#### Regression

    # Regression with linear model
    model_linear <- tl_model(mtcars, mpg ~ wt + hp, method = "linear")
    print(model_linear)
    #> tidylearn Model
    #> ===============
    #> Paradigm: supervised
    #> Method: linear
    #> Task: Regression
    #> Formula: mpg ~ wt + hp
    #>
    #> Training observations: 32

    # Predictions
    predictions_reg <- predict(model_linear)
    head(predictions_reg)
    #> # A tibble: 6 × 1
    #>   .pred
    #>   <dbl>
    #> 1  23.6
    #> 2  22.6
    #> 3  25.3
    #> 4  21.3
    #> 5  18.3
    #> 6  20.5

### Unsupervised Learning

#### Dimensionality Reduction

    # 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

#### Clustering

    # 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: 150

    # Get cluster assignments
    clusters <- model_kmeans$fit$clusters
    head(clusters)
    #> # A tibble: 6 × 2
    #>   .obs_id cluster
    #>   <chr>     <int>
    #> 1 1             3
    #> 2 2             3
    #> 3 3             3
    #> 4 4             3
    #> 5 5             3
    #> 6 6             3

    # Compare with actual species
    table(clusters$cluster, iris$Species)
    #>
    #>     setosa versicolor virginica
    #>   1      0          2        36
    #>   2      0         48        14
    #>   3     50          0         0

## Data Preprocessing

`tl_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:

    # Prepare data with multiple preprocessing steps
    processed <- tl_prepare_data(
      iris,
      Species ~ .,
      impute_method = "mean",
      scale_method = "standardize",
      encode_categorical = FALSE
    )
    #> Scaling numeric features using method: standardize

    # Check preprocessing steps applied
    names(processed$preprocessing_steps)
    #> [1] "scaling"

    # Use processed data for modeling
    model_processed <- tl_model(processed$data, Species ~ ., method = "forest")

## Train-Test Splitting

    # 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.3333333

## Wrapped Packages

tidylearn provides a unified interface to these established R packages:

### Supervised Methods

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()`

### Unsupervised Methods

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()`

### Accessing the Underlying Model

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) # nolint

An unsupervised method returns tidied components as well, so its `$fit` is the list holding them and the wrapped object sits at `$fit$model`:

    model_pca <- tl_model(iris, ~ ., method = "pca")
    names(model_pca$fit)
    #> [1] "scores"             "loadings"           "variance_explained"
    #> [4] "model"              "settings"
    class(model_pca$fit$model)  # This is the prcomp object
    #> [1] "prcomp"

## The Whole Workflow

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 %

## Next Steps

- `vignette("data-ingestion")` — reading from files, databases and cloud sources
- `vignette("supervised-learning")` — classification and regression in depth, and how to replay preprocessing on a test set
- `vignette("unsupervised-learning")` — clustering, ordination, and choosing the number of clusters
- `vignette("market-basket")` — association rules
- `vignette("tuning-and-pipelines")` — hyperparameter search, and bundling a workflow you can save
- `vignette("automl")` — searching across methods under a time budget
- `vignette("diagnostics")` — assumptions, influence and model comparison
- `vignette("reporting")` — plots and formatted `gt` tables
- `vignette("integration-workflows")` — combining supervised and unsupervised steps
- `vignette("compute-backends")` — when a fit is too slow or too large for this machine: GPU routing, cost estimates, and the cloud safety model