0
votes

SHORT SUMMARY

dplyr unquoting is failing as an argument of function summarise where the quoted object is the argument of a function the use of summarise, and that argument is assigned in a for loop.

For Loop

for(j in 1:1){
  sumvar  <- paste0("randnum",j)
  chkfunc(sumvar)
}

Function (abbreviated here, shown in full below)

chkfunc <- function(sumvar) {
sumvar <- enquo(sumvar)
[...]
    summarise(mn = mean(!!sumvar))

enter image description here

LONG SUMMARY

I have two columns that sometimes contain NAs and I want to use dplyr non-standard evaluation and its famous unquoting (AKA bang bang !!) to summarise each column in one for loop.

library(dplyr)
set.seed(3)
randnum1 <- rnorm(10)
randnum1[randnum1<0] <- NA
randnum2 <- rnorm(10)
randnum2[randnum2<0] <- NA
randfrm <- data.frame(cbind(randnum1, randnum2))
print(randfrm)

enter image description here

We see below that the filter function processes the unquoting (!!) just fine but the summarise function fails, returning an "argument is not numeric or logical" error. The same occurs when I use := in the summarise function call (not shown here), which appeared in the "Programming with dplyr" vignette. Finally, I confirmed that the class of !!sumvar is numeric within function chkfunc.

chkfunc <- function(sumvar) {
  sumvar <- enquo(sumvar)

  message("filter function worked with !!sumvar")
  outfrm <- randfrm %>%
    filter(!is.na(!!sumvar))
  print(outfrm)
  message("summarise function failed with !!sumvar")
  outfrm <- randfrm %>%
             filter(!is.na(!!sumvar)) %>%
             summarise(mn = mean(!!sumvar))
}
# Just one iteration to avoid confusion
for(j in 1:1){
  sumvar  <- paste0("randnum",j)
  chkfunc(sumvar)
}

enter image description here

1

1 Answers

0
votes

While I would like an answer using dplyr, the following works with substitute and eval rather than using dplyr functions (answer adapted from Akrun's answer to StackOverflow question "Unquote string in R's substitute command"):

chkfunc <- function(sumvar) {
  outfrm <- eval(substitute(randfrm %>%
                            filter(!is.na(y)) %>%
                            summarise(mn = mean(y)),
                            list(y=as.name(sumvar))))
  print(outfrm)
} 

for(j in 1:2){
  sumvar  <- paste0("randnum",j)
  chkfunc(sumvar)
}

print(outfrm)

enter image description here

Finally, I'll note that while the pull function on !!sumvar showed the resulting class to be numeric (i.e., the same class and values of randfrm$randnum1), I figured out that !!sumvar is treated as a character string (i.e., "randnum1) in both my use of filter and summarise, hence the argument is not numeric warning.