Functionals

Mini-Lecture 8

Ben Baumer

Smith College

2024-10-10

Slack review

The dots in base R

I wonder if the base function package in R have some kind of code that operates the dots?

  • The dots are base R!
  • The tidyverse doesn’t do anything special with the dots

Knowing how to pass

I’m still confused how R knows which function to pass ... to? It didn’t look like print.default() was mentioned in the body of print.factor() for example

  • Correct, but print() was called!
  • See also NextMethod()

Position of the dots

Does placement of ... matter? I mean I’m pretty sure it would, since we saw difference incidences where it’s placement varied.

  • Yes, placement matters
  • Most often (but not always), the dots are at the end.
library(tidyverse)
args(select)
function (.data, ...) 
NULL
args(map)
function (.x, .f, ..., .progress = FALSE) 
NULL
args(filter)
function (.data, ..., .by = NULL, .preserve = FALSE) 
NULL

Accepting and passing the dots

In the scale_color_smith() example, why do we need ... in both the outer and inner functions?

  • First, we need to accept the dots
  • Then, we need to pass them

Or, we can accept the dots and not pass them

  • The dots are “currently ignored”
my_fun <- function(...) {
  print("hi")
}
my_fun()
[1] "hi"

But we can’t not accept the dots and then try to pass them

“‘…’ used in an incorrect context”

my_fun <- function() {
  print(...)
}
my_fun()
Error in my_fun(): '...' used in an incorrect context

Capture the dots and pass only some of them

can you split up the arguments that are captured by the dots?

  • Yup.
eat_dots <- function(...) {
  dots <- list(...)
  if ("eat" %in% names(dots)) {
    dots[["eat"]] <- NULL
  }
  print(dots)
}
eat_dots(eat = "bagel", pass = "this")
$pass
[1] "this"

purrr

Highlights

You need to know:

  • map()

  • map2()

  • pmap()

  • walk()

Don’t worry as much about:

  • modify()

  • imap()

  • reduce()

  • accumulate()

map() (is just lapply())

map() with scalar second argument

map() with vector second argument

map() examples

map_int(starwars$films, length)
 [1] 5 6 7 4 5 3 3 1 1 6 3 2 5 4 1 3 3 1 5 5 3 1 1 2 1 2 1 1 1 1 1 3 1 3 2 1 1 1
[39] 2 1 1 2 1 1 3 1 1 1 1 3 3 3 2 2 2 1 3 2 1 1 1 2 2 1 1 2 2 1 1 1 1 1 1 2 1 1
[77] 2 1 1 2 2 1 1 1 1 1 1
map_chr(mpg, typeof)
manufacturer        model        displ         year          cyl        trans 
 "character"  "character"     "double"    "integer"    "integer"  "character" 
         drv          cty          hwy           fl        class 
 "character"    "integer"    "integer"  "character"  "character" 
# infix operator!
map(1:3, `^`, 2)
[[1]]
[1] 1

[[2]]
[1] 4

[[3]]
[1] 9

map2() with scalar third argument

map2() examples

starwars |>
  select(species, sex, gender) |>
  head(5)
# A tibble: 5 × 3
  species sex    gender   
  <chr>   <chr>  <chr>    
1 Human   male   masculine
2 Droid   none   masculine
3 Droid   none   masculine
4 Human   male   masculine
5 Human   female feminine 
cis <- function(sex, gender, num_chars) {
  stringr::str_sub(sex, 1, num_chars) == stringr::str_sub(gender, 1, num_chars)
}
map2_lgl(starwars$sex, starwars$gender, cis, num_chars = 1)
 [1]  TRUE FALSE FALSE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE
[13]  TRUE  TRUE  TRUE FALSE  TRUE    NA  TRUE  TRUE  TRUE FALSE  TRUE  TRUE
[25]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
[37]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
[49]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE    NA    NA
[61]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
[73]  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE    NA  TRUE  TRUE  TRUE
[85]  TRUE FALSE  TRUE

Example (cont’d)

map2_lgl(starwars$sex, starwars$gender, cis, num_chars = 3)
 [1] FALSE FALSE FALSE FALSE  TRUE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE
[13] FALSE FALSE FALSE FALSE FALSE    NA FALSE FALSE FALSE FALSE FALSE FALSE
[25] FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE
[37] FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE  TRUE FALSE FALSE FALSE
[49] FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE    NA    NA
[61] FALSE FALSE  TRUE  TRUE  TRUE FALSE FALSE FALSE  TRUE FALSE FALSE  TRUE
[73]  TRUE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE    NA FALSE FALSE  TRUE
[85] FALSE FALSE  TRUE
map2_dbl(1:3, 0:2, `^`)
[1] 1 2 9

walk2() example from real life!

# img is a named list of 23 ggplots
to_print <- tibble(
  name = names(img), 
  img = img,
  filename = paste0(name, ".jpg"),
  path = fs::path(here::here(filename))
)

walk2(
  to_print$img, to_print$path,
  ggsave, width = 10, height = 6, dpi = 600
)

pmap()

pmap() example

args(cis)
function (sex, gender, num_chars) 
NULL
starwars |>
  select(sex, gender) |>
  pmap_lgl(cis, num_chars = 1)
 [1]  TRUE FALSE FALSE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE
[13]  TRUE  TRUE  TRUE FALSE  TRUE    NA  TRUE  TRUE  TRUE FALSE  TRUE  TRUE
[25]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
[37]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
[49]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE    NA    NA
[61]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
[73]  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE    NA  TRUE  TRUE  TRUE
[85]  TRUE FALSE  TRUE

Another pmap() example

args(rnorm)
function (n, mean = 0, sd = 1) 
NULL
params <- tibble(
  mean = -1:1,
  sd = c(0.5, 1, 2)
)

pmap(params, rnorm, n = 1000) |>
  map_dbl(mean)
[1] -0.99051563  0.02764004  0.91739901

Now

Work on

  • Lab #8: Map

  • Reading quiz on Moodle