Search code examples
rpurrrtidyevalquosure

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


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)

Solution

  • 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, ...)
    }