lmfit <- lm(mpg~., data=mtcars)Stephen Turner
July 25-28, 2022
2020: RStudio became a Public Benefit Corporation, recognized as a Certified B Corporation (blog post).
Long-term focus on sustainable open-source software development for data science.
What will they keynote at rstudio::conf 2122 look like?
RStudio is becoming more multilingual (see also Quarto)
R, Python, Julia, etc. may be long-dead languages.
2022: RStudio becomes Posit
Blog post: rstudio.com/blog/rstudio-is-becoming-posit
More info: https://posit.co/
Compare base R versus tidymodels for a simple linear regression:
Set up workflow:
# Boosted tree spec
xgb_spec <-
boost_tree(
trees = 500, min_n = tune(),
stop_iter = tune(), tree_depth = tune(),
learn_rate = tune(), loss_reduction = tune()
) %>%
set_mode("classification") %>%
set_engine("xgboost")
# Boosted tree recipe
xgb_rec <-
recipe(on_goal ~ ., data = nhl_train) %>%
step_lencode_mixed(player, outcome=vars(on_goal)) %>%
step_dummy(all_nominal_predictors()) %>%
step_zv(all_predictors())
# Boosted tree workflow
xgb_wflow <-
workflow() %>%
add_model(xgb_spec) %>%
add_recipe(xgb_rec)Run this:
Copied to the clipboard:
ranger_recipe <-
recipe(formula = mpg ~ ., data = mtcars)
ranger_spec <-
rand_forest(mtry = tune(), min_n = tune(), trees = 1000) %>%
set_mode("regression") %>%
set_engine("ranger")
ranger_workflow <-
workflow() %>%
add_recipe(ranger_recipe) %>%
add_model(ranger_spec)
set.seed(38757)
ranger_tune <-
tune_grid(ranger_workflow,
resamples = stop("add your rsample object"),
grid = stop("add number of candidate points"))Run this:
Copied to the clipboard:
glmnet_recipe <-
recipe(formula = mpg ~ ., data = mtcars) %>%
step_zv(all_predictors()) %>%
step_normalize(all_numeric_predictors())
glmnet_spec <-
linear_reg(penalty = tune(), mixture = tune()) %>%
set_mode("regression") %>%
set_engine("glmnet")
glmnet_workflow <-
workflow() %>%
add_recipe(glmnet_recipe) %>%
add_model(glmnet_spec)
glmnet_grid <- tidyr::crossing(penalty = 10^seq(-6, -1, length.out = 20),
mixture = c(0.05, 0.2, 0.4, 0.6, 0.8, 1))
glmnet_tune <-
tune_grid(glmnet_workflow,
resamples = stop("add your rsample object"),
grid = glmnet_grid) Run this:
Copied to the clipboard:
xgboost_recipe <-
recipe(formula = mpg ~ ., data = mtcars) %>%
step_zv(all_predictors())
xgboost_spec <-
boost_tree(trees = tune(), min_n = tune(),
tree_depth = tune(), learn_rate = tune(),
loss_reduction = tune(), sample_size = tune()) %>%
set_mode("regression") %>%
set_engine("xgboost")
xgboost_workflow <-
workflow() %>%
add_recipe(xgboost_recipe) %>%
add_model(xgboost_spec)
set.seed(9270)
xgboost_tune <-
tune_grid(xgboost_workflow,
resamples = stop("add your rsample object"),
grid = stop("add number of candidate points"))Model training and deployment. Do this in one R session.
library(tidymodels)
library(vetiver)
library(plumber)
# Not the way you'd actually split data - for demo only
cars_train <- mtcars[1:24,]
cars_test <- mtcars[25:32,]
# Random forest using C++ implementation
rf_spec <-
rand_forest(trees=1000) %>%
set_mode("regression") %>%
set_engine("ranger")
# Simple workflow
rf_wflow <- workflow(mpg~., rf_spec)
# Fit the model
rf_fit <- fit(rf_wflow, cars_train)
# Create a vetiver model object
v <- vetiver_model(rf_fit, "mtcars_mpg")
# Create a plumber API
pr <- pr() %>% vetiver_api(v)
# Run the API server, open at http://127.0.0.1:5678/
pr_run(pr, port=5678)Predict from the model endpoint. Do this in a separate session.
library(vetiver)
# Use the same IP/port from above
endpoint <- vetiver_endpoint("http://127.0.0.1:5678/predict")
# same split from above
cars_test <- mtcars[25:32,]
# Predict from the endpoint (returns a 1-col tibble)
predict(endpoint, cars_test)
# Stick this onto the original data, plot, etc.
cbind(cars_test, predict(endpoint, cars_test))Consider simple prep-processing (e.g., centering/scaling numeric predictors), simple feature engineering (e.g., PCA), followed by vanilla GLM/tree model.
Wrong:

Tidymodels makes this easy:

See Alison Hill’s blog post from April
quarto is a language agnostic command line interface (CLI).quarto CLI installed. Usage: quarto
Version: 1.0.36
Options:
-h, --help - Show this help.
-V, --version - Show the version number for this program.
Commands:
render [input] [args...] - Render input file(s) to various document types.
preview [file] [args...] - Render and preview a document or website project.
serve [input] - Serve a Shiny interactive document.
create-project [dir] - Create a project for rendering multiple documents
convert <input> - Convert documents to alternate representations.
pandoc [args...] - Run the version of Pandoc embedded within Quarto.
run [script] [args...] - Run a TypeScript, R, Python, or Lua script.
install <type> [target] - Installs an extension or global dependency.
publish [provider] [path] - Publish a document or project. Available providers include:
check [target] - Verify correct functioning of Quarto installation.

.qmd is a plain text file. Regular Rmarkdown mostly “just works”#| echo: false)Similar conceptually to Shiny for R, and designed to feel like idiomatic Python
Documentation: https://shiny.rstudio.com/py.
Deployment options include shinyapps.io, RStudio Connect, Shiny Server Open Source, and on a static server.
Minimal example:
URL: stephenturner.github.io/shinypy-helloworld
Code: github.com/stephenturner/shinypy-helloworld
from shiny import App, render, ui
import numpy as np
import matplotlib.pyplot as plt
app_ui = ui.page_fluid(
ui.layout_sidebar(
ui.panel_sidebar(
ui.input_slider("n", "N", 0, 100, 20),
),
ui.panel_main(
ui.output_plot("plot"),
),
),
)
def server(input, output, session):
@output
@render.plot(alt="A histogram")
def plot():
np.random.seed(19680801)
x = 100 + 15 * np.random.randn(437)
fig, ax = plt.subplots()
ax.hist(x, input.n(), density=True)
return fig
app = App(app_ui, server, debug=True)ref.label option. New opts.label="prev-chunk" re-uses a labeled chunk’s options (can overwrite in current chunk’s options).{r, file=c("script-1.R")} instead of {r, code=readLines("script.R")} to use the contents of script.R as the content of this chunk.Chunk options can be written inside a code chunk after #|, e.g.,
#| echo = FALSE, fig.width = 10,
#| fig.cap = "This is a long caption."
Or using YAML. Convert old chunk options in a .Rmd to the new syntax with knitr::convert_chunk_header().
#| echo: false
#| fig.width: 10
Mark Rieke at Memorial Hermann Health System introduced the **workboots** package for generating prediction intervals in the tidymodels ecosystem. Documentation/source: markjrieke.github.io/workboots. Example usage:
Create a workflow with tidymodels:
library(tidymodels)
data("penguins")
penguins <- penguins %>% drop_na()
set.seed(123)
penguins_split <- initial_split(penguins)
penguins_test <- testing(penguins_split)
penguins_train <- training(penguins_split)
penguins_wf <-
workflow() %>%
add_recipe(
recipe(body_mass_g ~ ., data = penguins_train) %>%
step_dummy(all_nominal())) %>%
add_model(boost_tree("regression"))There are thousands of R packages - how do you go about vetting & choosing which packages to integrate into your workflow?
Things to look for:
Community – how active is the development/user community around the package?
Documentation – how well documented is the package?
Tests – does the package have good built-in tests (testthat)?
Authors/creators – are creators grad students/postdocs who will abandon the project when it’s done? Would you anticipate long-term development and support?
Isaac Florence from the UK Health Security Agency talked about scaling and automating R workflows with Kubernetes and Airflow.
UKHSA using RH OpenShift implementation of k8s to deploy containers at whatever scale on whatever hardware available (cloud, laptop, standard HPC, etc).
Airflow (also open source) from Apache is a workflow scheduler and monitoring platform aka “fancy cron.”
At UKHSA, each team has own k8s namespace (“project” in OpenShift), which has defined users, resources, and security.
Each Airflow DAG is assigned to a project so teams can have multiple DAGs. Teams can see their own projects and DAGs only in Airflow, aka simplicity/security.
Every Airflow task creates a new pod from a container image, specifying a terminal command (eg R script). Specs of images/secrets/credentials/etc all defined in k8s - airflow just tells k8s what pod to run and when.
Clock is not replacing lubridate.
Improved safety with time zones, calendars, etc. E.g., "2022-01-30" + months(1)
New date types: year_month_day to year_quarter_day year_week_day, etc.
Clock is compatible with the slider package (rolling averages) and ivs (ranges).