is_quosure(x) error when forwarding ... inside map

114 Views Asked by At

I would like to define a wrapper to an inner function. The idea is to repeat random sampling that uses one of r* base function (eg runif, rnorm, etc.) and let the user easily change this inner function and define custom ones.

The example below show a reproducible example that I cannot make work with tidyeval patterns and, more precisely, within a purrr::map. The evaluation of ... seems to not happen properly. I missed something on quosures evaluation but I cannot figure what. I also show below a workaround that works fine with a goold old replicate.

I would like to implement such behaviour in more complex cases and, more generally, be delighted for any pointer and to understand why the following does not work.

# use the tidyverse and define a dummy tibble
library(tidyverse)
df <- tibble(col1=seq(10, 50, 10), col2=col1+5)

# first random function, on top of stats::runif
random_1 <- function(x, min, max){
  x %>% 
    rowwise() %>% 
    mutate(new=runif(1, min={{min}}, max={{max}})) %>% 
    ungroup()
}

# second random function, on top of stats::rnorm
random_2 <- function(x, mean, sd){
  x %>% 
    rowwise() %>% 
    mutate(new=rnorm(1, mean={{mean}}, sd={{sd}})) %>% 
    ungroup()
}

# at top level, everything works fine
> df %>% random_1(min=col1, max=col2)
> df %>% random_2(mean=col1, sd=col2)

# So far so good
# we we wrap it for a single shot
random_fun <- function(x, random_fun, ...){
  random_fun(x, ...)
}

random_fun(df, random_1, min=col1, max=col2)

# Still fine.
# Here comes the trouble:
random_fun_k <- function(df, k, random_fun, ...){
  map(1:k, ~random_fun(df, ...))
}

random_fun_k(df, k=2, random_1, min=col1, max=col2)

Error in is_quosure(x) : argument "x" is missing, with no default

The following workaround around replicate works fine yet I would like to stick to tidyeval spirit:

random_fun_k_oldie <- function(df, k, random_fun, ...){
  f <- random_fun(df, ...)
  replicate(k, f, simplify=FALSE)
}
random_fun_k_oldie(df, k=2, random_1, min=col1, max=col2)
random_fun_k_oldie(df, k=2, random_2, mean=col1, sd=col2)
2

There are 2 best solutions below

1
akrun On BEST ANSWER

It may be better to use original lambda function i.e. function(x)

library(purrr)
random_fun_k <- function(df, k, random_fun, ...){
   map(seq_len(k), function(x) random_fun(df, ...))
 }

-testing

> random_fun_k(df, k=2, random_1, min=col1, max=col2)
[[1]]
# A tibble: 5 × 3
   col1  col2   new
  <dbl> <dbl> <dbl>
1    10    15  12.6
2    20    25  21.4
3    30    35  34.1
4    40    45  40.7
5    50    55  53.8

[[2]]
# A tibble: 5 × 3
   col1  col2   new
  <dbl> <dbl> <dbl>
1    10    15  13.1
2    20    25  24.2
3    30    35  33.8
4    40    45  41.6
5    50    55  50.9

NOTE: The function name and argument name seems to be the same rand_fun and this could cause some confusion as well (though it is not the source of the error). It may be better to rename the function argument differently

random_fun <- function(x, rn_fun, ...){
  rn_fun(x, ...)
}
0
Lionel Henry On

purrr's lambdas support positional arguments with the ..1, ..2, etc syntax. This is implemented via the ... mechanism. Because of this, you're not passing the correct arguments to random_fun.

The solution is to use a normal lambda function as akrun suggested. Maybe you could use the \(x) x syntax of R 4.0.