bookclub-advr

DSLC Advanced R Book Club
git clone https://git.eamoncaddigan.net/bookclub-advr.git
Log | Files | Refs | README | LICENSE

commit a045ef2662f61ba6aaea00cffa281bcc8b39679b
parent e16a6ac5c83c339a473be2e0c55ba0a22f257c26
Author: Arthur Shaw <47256431+arthur-shaw@users.noreply.github.com>
Date:   Wed, 16 Nov 2022 15:35:58 -0500

Draft notes (#35)


Diffstat:
M19_Quasiquotation.Rmd | 220+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 216 insertions(+), 4 deletions(-)

diff --git a/19_Quasiquotation.Rmd b/19_Quasiquotation.Rmd @@ -2,12 +2,224 @@ **Learning objectives:** -- THESE ARE NICE TO HAVE BUT NOT ABSOLUTELY NECESSARY +- What quasiquotation is +- Why it might be useful +- How it works +- What are some common use cases -## SLIDE 1 +## What is quasiquotation? + +- **Quote.** Stop evaluation of an expression--typically, a function argument. (In `{rlang}` jargon, defuse.) +- **Unquote.** Evaluate a quoted expression--typically, inside a function. (In `{rlang}` jargon often, inject) + +## Why use quasiquotation? + +- **Make functions tidy.** Provide function arguments bare names. +```{r} +#| eval: FALSE +# `cyl` is written as a bare name--a symbol defined in the global environment +# but `cyl` only exists in the data frame "environment" +# so, `{dplyr}` quotes the argument +dplyr::filter(mtcars, cyl == 4) +``` +- **Make functions Shiny-friendly.** Provide names of columns as strings. +```{r} +#| eval = FALSE +# imagine that `var` came from `input$var_you_selected` +# this "quoted" column name is transformed into a bare name below +my_mean <- function(df, var) { + dplyr::mutate(df, mean = mean(.data[[var]], na.rm = TRUE)) +} +``` +- **Inject user-written into a function's pipeline.** For example: summarize expressions, rename expressions, etc. + +## How use quasiquotation? + +Let's look at its components: + +- Quotate +- Unquote +- Quote and unquote, in one go + +## Quote + +- Expressions +```{r} +#| label:quote_expressions +#| eval:TRUE + +# 1. rlang::expr + +# for interactive use +rlang::expr(x+y) + +# but not for use in a function +# see below + +# 2. rlang::enexpr + +# expr doesn't yield desired result +f1 <- function(x) rlang::expr(x) +f1(a + b + c) + +# enexpr does, since it maintains reference to environment where x is defined +f2 <- function(x) rlang::enexpr(x) +f2(a + b + c) +``` +- Symbols +```{r} +#| label:quote_symbols +#| eval:TRUE + +# captures a list of symbols or strings in ... +# returns a list of symbols +f <- function(...) { + ensyms(...) +} + +# case 1: input = symbol +f(x) +# case 2: input = character string +f("x") +``` + +## Unquote + +- One argument +```{r} +#| label:unquote_one_arg +#| eval:TRUE + +# quote `-1` as `x` +x <- rlang::expr(-1) +# unquote `x` to substitute its unquoted value +# use bang-bang operator +rlang::expr(f(!!x, y)) +``` +- Multiple arguments +```{r} +#| label:unquote_mult_args +#| eval:TRUE + +# quote multiple args--note the `s` +xs <- rlang::exprs(1, a, -b) +# unquote multiple arguments +# use bang-bang-bang operator +expr(f(!!!xs, y)) +``` + +## Quote and unquote + +- **Single argument.** Embrace the simplicity of the embrace operator `{{` +```{r} +#| label: embrace +#| eval: FALSE + +# The hard way +my_hard_summarize <- function(data, var) { + # defuse (aka quote) + var <- rlang::enquo(var) + + # inject (aka unquote) + dplyr::summarize(data, mean = mean(!!var, na.rm = TRUE)) + +} + +# The easy way +my_easy_summarize <- function(data, var) { + + # in one move, quote and unquote `var` + dplyr::summarize(data, mean = mean({{var}}, na.rm = TRUE)) + +} + +``` +- **Multiple arguments.** Just pass the (dynamic) dots when you can. Otherwise, quote (e.g., `rlang::enquos`) and splice (`!!!`). +```{r} +#| label: dots + +# The easy way for easy cases of simple "forwarding" +my_group_by <- function(.data, ...) { + .data %>% dplyr::group_by(...) +} + +mtcars %>% my_group_by(cyl = cyl * 100, am) + +# The harder way remains the solution for non-forwarding use cases +# There is no plural version of the embrace operator `{{` +``` + + +## Usage + +- Data masking +- Tidy selection +- Metaprogramming + +## Data masking + +My (data) pronoun is: + +- `.data`. Object defined in data frame. +- `.env`. Object defined in (global) environment + +To see why this distinction is needed, consider: + +```{r} +#| label: data_masking +#| eval: TRUE +cyl <- 1000 + +mtcars %>% + dplyr::summarise( + mean_data = mean(.data$cyl), + mean_env = mean(.env$cyl) + ) +``` + +Read more [here](https://rlang.r-lib.org/reference/topic-data-mask.html) and [here](https://rlang.r-lib.org/reference/topic-data-mask-ambiguity.html). + +## Tidy selection + +```{r} +#| label: tidy_selection +#| eval: FALSE + +my_cols <- c("wt", "mpg", "not_in_mtcars") + +# note: `tidyselect::all_of()` errors if any column not found +mtcars |> select(all_of(placeholder)) |> glimpse() + +# note: `tidyselect::any_of()` excludes requested columns not found +mtcars |> select(any_of(placeholder)) |> glimpse() +``` + + +## Metaprogramming + +A few common patterns: + +- **Forwarding.** Defuse and inject. Think embrace operator. See [here](https://rlang.r-lib.org/reference/topic-metaprogramming.html#forwarding-patterns). +- **Names.** Symbolize and inject. Think: character -> unevaluated symbol -> evaluated symbol. See [here](https://rlang.r-lib.org/reference/topic-metaprogramming.html#names-patterns). +- **Bridge.** Capture user inputs -> transform into two representations -> evaluate each representation. See [here](https://rlang.r-lib.org/reference/topic-metaprogramming.html#bridge-patterns). +- **Transformation.** Capture user inputs -> compose unevaluated call -> evaluate call. See more [here](https://rlang.r-lib.org/reference/topic-metaprogramming.html#transformation-patterns) + +See more [here](https://rlang.r-lib.org/reference/topic-metaprogramming.html). + +## For further study + +TLDR: + +- Watch videos compiled [here](https://rstudio-conf-2022.github.io/build-tidy-tools/materials/day-2-session-3-tidy-eval.html#/additional-material). +- Peruse modern solutions in [slides](https://rstudio-conf-2022.github.io/build-tidy-tools/materials/day-2-session-3-tidy-eval.html#/title-slide) from rstudio::conf(2022) workshop on tidy tools. + +Have time; will read: + +- [Programing with dplyr](https://dplyr.tidyverse.org/articles/programming.html#use-a-variable-from-an-shiny-input) +- [Using ggplot2 in packages](https://ggplot2.tidyverse.org/articles/ggplot2-in-packages.html) +- `{rlang}` vignettes +- Guide to tidy eval. Superceded, but nice place to find use cases and past solutions. Archived repo [here](https://github.com/tidyverse/tidyeval). Book built by someone else [here](https://bookdown.dongzhuoer.com/tidyverse/tidyeval/). -- ADD SLIDES AS SECTIONS (`##`). -- TRY TO KEEP THEM RELATIVELY SLIDE-LIKE; THESE ARE NOTES, NOT THE BOOK ITSELF. ## Meeting Videos