making loop-friendly formula interface in tidyeval functions

Viewed 69

I am writing a simple function using tidyeval where I need to pass the arguments to the formula interface. Although I have managed to build a working version of the function, it doesn't seem to work with for loops.

function

foo <- function(data, x, y) {
  BayesFactor::ttestBF(
    paired = FALSE,
    data = data,
    formula = rlang::new_formula(rlang::enexpr(y), rlang::enexpr(x))
  )
}

foo(mtcars, am, wt)
#> Bayes factor analysis
#> --------------
#> [1] Alt., r=0.707 : 1383.367 ±0%
#> 
#> Against denominator:
#>   Null, mu1-mu2 = 0 
#> ---
#> Bayes factor type: BFindepSample, JZS

working with loops

I also tried here !!col.name[i]

df <- dplyr::select(mtcars, am, wt, mpg)
col.name <- colnames(df)

for (i in 2:length(col.name)) {
  foo(
    data = mtcars,
    x = am,
    y = col.name[i]
  )
}
#> Error in `[.data.frame`(data, , dv): undefined columns selected
2 Answers

If you want to make a data-masking function work with loops over columns, you have to do metaprogramming at some point.

Really there are two options:

  • Either make your function take strings with standard evaluation. Then transform that string to a symbol internally. The metaprogramming is internal.

  • Or make it take expressions with non-standard evaluation. Then your callers have to transform strings to symbols and unquote them. The metaprogramIng is external.

There is no way around that, unless you're going to create a non standard interface that works inconsistently and unpredictably by trying to be too magical.

If you can pass string values as variables we can use reformulate to construct the formula.

foo <- function(data, x, y) {
  BayesFactor::ttestBF(
    paired = FALSE,
    data = data,
    formula = reformulate(x, y)
  )
}

foo(mtcars, "am", "wt")

#Bayes factor analysis
#--------------
#[1] Alt., r=0.707 : 1383.367294 ±0%

#Against denominator:
#  Null, mu1-mu2 = 0 
#---
#Bayes factor type: BFindepSample, JZS

To pass it in a loop/lapply :

col.name <- c('wt', 'mpg')
result <- lapply(col.name, foo, data = mtcars, x = 'am')
result

#[[1]]
#Bayes factor analysis
#--------------
#[1] Alt., r=0.707 : 1383.367294 ±0%

#Against denominator:
#  Null, mu1-mu2 = 0 
#---
#Bayes factor type: BFindepSample, JZS


#[[2]]
#Bayes factor analysis
#--------------
#[1] Alt., r=0.707 : 86.58972736 ±0%

#Against denominator:
#  Null, mu1-mu2 = 0 
#---
#Bayes factor type: BFindepSample, JZS
Related