Search code examples
rdataframefunctiondplyrrlang

Using dplyr::arrange() inside an R function


I wonder why my_function() below doesn't arrange_by= the variable given to it?

For example, try: my_function(DATA, study, time, treat_grp, arrange_by = "treat_grp") OR my_function(DATA, study, time, treat_grp, arrange_by = "time") and there is no arranging happening.

Is there any fix to this?

library(tidyverse)

my_function <- function(data, cluster, ..., arrange_by = NULL) {
  cluster <- rlang::ensym(cluster)
  cat_mod <- rlang::ensyms(...)
  cat_nms <- purrr::map_chr(cat_mod, rlang::as_string)  
  
  data %>% 
    count(!!cluster, !!!rlang::syms(cat_nms)) %>% 
    group_by(!!!rlang::syms(cat_nms)) %>% 
    summarize(`n study` = n(),
              `n effect` = sum(n)) %>% 
    ungroup() %>%  
    tidyr::complete(!!!rlang::syms(cat_nms), fill = list(`n study` = 0, `n effect` = 0)) %>% 
    arrange(if(is.null(arrange_by)) all_of(cat_nms[1]) else all_of(arrange_by))
}

#--------------------------------------------------
DATA <- structure(
  list(
    study = c(1, 1, 1, 1, 1, 1, 2, 2, 3, 4, 4, 4, 4, 5, 5),
    time = c(
      "baseline", "posttest1", "posttest2", "baseline", "posttest1",
      "posttest2", "posttest1", "posttest1", "posttest1", "posttest1",
      "posttest1", "posttest1", "posttest1", "posttest1", "posttest2"
    ),
    treat_grp = c(
      "conventional", "conventional", "conventional", "conventional",
      "conventional", "conventional", "conventional", "framework_notes",
      "conventional", "conventional", "conventional", "conventional",
      "conventional", "vocabulary_notebook", "vocabulary_notebook"
    )
  ), 
  row.names = c(NA, -15L), 
  class = "data.frame"
)

# these function calls don't arrange anything
my_function(DATA, study, time, treat_grp, arrange_by = "treat_grp")
my_function(DATA, study, time, treat_grp, arrange_by = "time")

Solution

  • You need to use across() with your arrange() or arrange_at() line. For example

    arrange(across(if(is.null(arrange_by)) all_of(cat_nms[1]) else all_of(arrange_by)))
    # or 
    arrange_at(if(is.null(arrange_by)) all_of(cat_nms[1]) else all_of(arrange_by))
    

    With a simpler example

    iris %>% arrange(Sepal.Width) %>% head()
    iris %>% arrange(all_of("Sepal.Width")) %>% head()
    iris %>% arrange(across(all_of("Sepal.Width"))) %>% head()
    iris %>% arrange_at("Sepal.Width") %>% head()
    

    You can see the arrange only works for lines 1 3 and 4, not 2.