In R's purrr, why does pmap through Error in FUN(X[[i]], ...) : object '.z' not found when .z is clearly defined?

225 Views Asked by At

I'm trying to get better with pmap(), but I'm hitting some errors. In this example, I have a simple graphing function that I want to iterate over. My understanding of pmap() is that I can set and define an infinite number of parameters. However, I'm confused why it is saying that .z isn't defined when I so clearly have it defined.

Unless necessary, I'm not interested in changing any of the ways the arguments are defined--I just want to understand and fix why I can't have this 3rd argument, even though .x and .y work fine (even if I switch around what is defined as .x, .y, and .z).

library(purrr)
library(ggplot2)
library(dplyr)

#Plot function

make_chart <- function(data, x, y, xtitle){
  
  require(stringr)
    
  ggplot(data, aes(x = as.factor({{x}}), y = {{y}})) +
    geom_col() +
    ggtitle(paste0("Number of ", str_to_title({{xtitle}}), " by MPG")) +
    xlab({{xtitle}})
  
}

#Define x variables
x_variables <- c("cyl", "vs", "am", "gear", "carb")


#pmap it--why is .z not found and how do I get it to be?

pmap(list(.x = mtcars %>% dplyr::select(matches(x_variables)),
          .y = x_variables,
          .z = mtcars %>% dplyr::select(mpg)),
     ~mtcars %>%
       make_chart(x = .x, xtitle = .y, y = .z))
2

There are 2 best solutions below

1
stefan On BEST ANSWER

Another option to the ones offered by @shafee would be to pass a named list to pmap with the names of the function arguments. Doing so we don't need an anonymous function which merely maps the names of the list passed to pmap to the names of the function arguments.

Moreover, at least from a ggplot2 standpoint best practice to create your loop would be to loop over the column names (and making use of the .data pronoun) instead of passing vectors to your function. Actually, doing so you could get rid of the xtitle argument and replace xtitle by x in the plotting function.

library(purrr)
library(ggplot2)
library(stringr)

make_chart <- function(data, x, y, xtitle) {
  ggplot(data, aes(x = as.factor(.data[[x]]), y = .data[[y]])) +
    geom_col() +
    ggtitle(paste0("Number of ", str_to_title(xtitle), " by MPG")) +
    xlab(xtitle)
}

x_variables <- c("cyl", "vs", "am", "gear", "carb")

pmap(
  list(
    x = x_variables,
    xtitle = x_variables,
    y = "mpg"
  ),
  make_chart,
  data = mtcars
)
#> [[1]]

#> 
#> [[2]]

0
shafee On

from ?pmap

pmap(.l, .f, ..., .progress = FALSE)

.l => A list of vectors. The length of .l determines the number of arguments that .f will be called with. Arguments will be supplied by position if unnamed, and by name, if named.

Either use an anonymous function to supply arguments to make_chart by name from the list (preferred way) or supply arguments by position using formula syntax,

# using anonymous function to supply argument by name
pmap(.l = list(x = mtcars %>% dplyr::select(matches(x_variables)),
          y = x_variables,
          z = mtcars %>% dplyr::select(mpg)),
     .f = \(x, y, z) mtcars %>% make_chart(x = x, xtitle = y, y = z))

or,

# supplying arguments by position
pmap(.l = list(mtcars %>% dplyr::select(matches(x_variables)),
          x_variables,
          mtcars %>% dplyr::select(mpg)),
     .f = ~ mtcars %>% make_chart(x = ..1, xtitle = ..2, y = ..3))