knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  fig.path = "README-"
)

pipelearner makes it easy to create machine learning pipelines in R.

Installation and background

pipelearner is currently available from github as a development package only. It can be installed by running:

# install.packages("devtools")
devtools::install_github("drsimonj/pipelearner")

pipelearner is built on top of tidyverse packages like modelr. To harness the full power of pipelearner, it will help to possess some technical knowledge of tidyverse tools such as:

An excellent resource to get started with these is R for Data Science, by Garrett Grolemund and Hadley Wickham.

API

Similar to the way ggplot2 elements are layered with +, you initialize and customize a pipelearner object, which is a list, with functions that can be piped into eachother with %>%. Rather than plotting, however, a pipelearner then learns.

Initialize a pipelearner object with:

Customize a pipelearner with:

Learn (fit) everything and obtain a tibble of results with:

Initialization

The following initializes a pipelearner object that will use the iris data set and linear regression (lm) to learn how to predict Sepal.Length with all other available variables (Sepal.Length ~ .).

library(pipelearner)

pl <- pipelearner(iris, lm, Sepal.Length ~ .)

Print a pipelearner object to expose the list elements.

pl
Defaults to note

Learning

Once a pipelearner is setup, use learn() to fit all models to every combination of training proportions (train_ps) and set of training data in the cross-validation pairs (cv_pairs), and return a tibble of the results.

pl %>% learn()
Quick notes

Cross-validation pairs

Cross-validation pairs can be customized with learn_cvpairs(). The following implements k-fold cross validation with k = 4.

pl %>%
  learn_cvpairs(crossv_kfold, k = 4) %>% 
  learn()

Notice the five rows where the model has been fitted to training data for each fold, represented by cv_pairs.id. The precise training data sets are also stored under train.

Learning curves

Learning curves can be customized wth learn_curves(). The following will fit the model to three proportions of the training data (.5, .75, and 1):

pl %>% 
  learn_curves(.5, .75, 1) %>% 
  learn()

Notice the three rows where the model has been fitted to the three proportions of the training data, represented by train_p. Again, train contains references to the precise data used in each case.

More models

Add more models with learn_models(). For example, the following adds a decision tree to be fitted:

pl %>% 
  learn_models(rpart::rpart, Sepal.Length ~ .) %>% 
  learn()

Notice two rows where the regression and decision tree models have been fit to the training data, represented by models.id. The different model calls also appear under model.

Things to know about learn_models():

For example, the following fits two models with three formulas:

pipelearner(iris) %>%
  learn_models(c(lm, rpart::rpart),
               c(Sepal.Length ~ Sepal.Width,
                 Sepal.Length ~ Sepal.Width + Petal.Length,
                 Sepal.Length ~ Sepal.Width + Petal.Length + Species)) %>% 
  learn()

The following fits a regression model and grid-searches hyperparameters of a decision tree:

pipelearner(iris) %>%
  learn_models(lm, Sepal.Length ~ .) %>% 
  learn_models(rpart::rpart, Sepal.Length ~ .,
               minsplit = c(2, 20), cp = c(0.01, 0.1)) %>% 
  learn()

Remember that these additional parameters (including different formulas) are contained under params.

Bringing it all together

After initialization, pipelearner functions can be combined in a single pipeline. For example, the following will:

iris %>% 
  pipelearner() %>%
  learn_cvpairs(crossv_mc, n = 50) %>%
  learn_curves(seq(.5, 1, by = .1)) %>% 
  learn_models(lm, Sepal.Width ~ .*.) %>% 
  learn_models(rpart::rpart, Sepal.Width ~ .) %>% 
  learn()

Beyond learning

As you can see, pipelearner makes it easy to fit many models. The next step is to extract performance metrics from the tibble of results. This is where prior familiarity working with tidyverse tools becomes useful if not essential.

At present, pipelearner doesn't provide functions to extract any further information. This is because the information to be extracted can vary considerably between the models fitted to the data.

The following will demonstrate an example of visualising learning curves by extracting performance information from regression models.

r_square() is setup to extract an R-squared value. It is based on modelr::rsquare, but adjusted to handle new data sets (I've submitted an issue to incorporate into modelr).

# R-Squared scoring (because modelr rsquare doen't work right now)
response_var <- function(model) {
  formula(model)[[2L]]
}
response <- function(model, data) {
  eval(response_var(model), as.data.frame(data))
}
r_square <- function(model, data) {
  actual    <- response(model, data)
  residuals <- predict(model, data) - actual
  1 - (var(residuals, na.rm = TRUE) / var(actual, na.rm = TRUE))
}

Using a subset of the weather data from the nycflights13 package, fit a single regression model to 50 cross-validation pairs, holding out 15% of the data for testing in each case, in iterative training proportions. Note heavy use of tidyverse functions.

library(tidyverse)

# Create the data set
library(nycflights13)
d <- weather %>%
  select(visib, humid, precip, wind_dir) %>% 
  drop_na() %>%
  sample_n(2000)

results <- d %>% 
  pipelearner() %>% 
  learn_cvpairs(crossv_mc, n = 50, test = .15) %>% 
  learn_curves(seq(.1, 1, by = .1)) %>% 
  learn_models(lm, visib ~ .) %>% 
  learn()
results

New columns are added with dplyr::mutate containing the rsquared values for each set of training and test data by using purrr functions.

results <- results %>% 
  mutate(
    rsquare_train = map2_dbl(fit, train, r_square),
    rsquare_test  = map2_dbl(fit, test,  r_square)
  )

results %>% select(cv_pairs.id, train_p, contains("rsquare"))

We can visualize these learning curves as follows:

results %>% 
  select(train_p, contains("rsquare")) %>% 
  gather(source, rsquare, contains("rsquare")) %>% 
  ggplot(aes(train_p, rsquare, color = source)) +
   geom_jitter(width = .03, alpha = .3) +
   stat_summary(geom = "line", fun.y = mean) +
   stat_summary(geom = "point", fun.y = mean, size = 4) +
   labs(x = "Proportion of training data used",
        y = "R Squared")

The example below fits a decision tree and random forest to 20 folds of a subset of the data.

results <-  d %>% 
  pipelearner() %>% 
  learn_cvpairs(crossv_kfold, k = 20) %>% 
  learn_models(c(rpart::rpart, randomForest::randomForest), 
               visib ~ .) %>% 
  learn()
results

Then compute R-Square statistics and visualize the results:

results %>% 
  mutate(rsquare_train = map2_dbl(fit, train, r_square),
         rsquare_test  = map2_dbl(fit, test,  r_square)) %>% 
  select(model, contains("rsquare")) %>% 
  gather(source, rsquare, contains("rsquare")) %>%
  ggplot(aes(model, rsquare, color = source)) +
   geom_jitter(width = .05, alpha = .3) +
   stat_summary(geom = "point", fun.y = mean, size = 4) +
   labs(x = "Learning model",
        y = "R Squared")


drsimonj/pipelearner documentation built on May 15, 2019, 2:53 p.m.