在R函数中使用dplyr::arrange()失效问题及修复咨询
问题:my_function无法按指定arrange_by参数排序
编写的my_function()无法按照指定的arrange_by=参数进行排序,例如调用my_function(DATA, study, time, treat_grp, arrange_by = "treat_grp")或my_function(DATA, study, time, treat_grp, arrange_by = "time")时,并未发生排序操作,请问如何修复?
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" ) # 这些调用没有产生排序效果 my_function(DATA, study, time, treat_grp, arrange_by = "treat_grp") my_function(DATA, study, time, treat_grp, arrange_by = "time")
问题原因
核心问题出在arrange()中的if语句用法:arrange(if(is.null(arrange_by)) all_of(cat_nms[1]) else all_of(arrange_by))。
if语句在这里返回的是字符串(如"treat_grp"),而arrange()需要的是列对象而非字符串。尽管all_of()可以将字符串转换为列引用,但嵌套在if语句中时,arrange()无法正确解析这个逻辑,导致排序操作失效。
修复方案
提供两种简洁可行的修复方式:
方式一:用rlang::sym()转换为符号后解引用
直接将字符串转为列符号,通过!!解引用后传入arrange():
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)) rlang::sym(cat_nms[1]) else rlang::sym(arrange_by)) }
方式二:提前确定排序列再传入
先通过条件判断得到目标排序列的字符串,再用all_of()传入arrange():
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) # 提前确定排序列 sort_col <- if(is.null(arrange_by)) cat_nms[1] else arrange_by 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(all_of(sort_col)) }
修复后重新调用函数,即可看到按指定列排序的结果:
# 按treat_grp排序 my_function(DATA, study, time, treat_grp, arrange_by = "treat_grp") # 按time排序 my_function(DATA, study, time, treat_grp, arrange_by = "time")
内容的提问来源于stack exchange,提问作者Simon Harmel
相关产品推荐
相关产品推荐

