问题
To make a custom function flexible to receiving one or more calling arguments per formal argument I currently rely on "...":
library(dplyr)
foo <- function(data, ..., dv){
groups <- enquos(...)
dv <- enquo(dv)
data %>%
group_by(!!!groups) %>%
summarise(group_mean = mean(!!dv))
}
mtcars %>% foo(am, dv = mpg)
mtcars %>% foo(vs, am, dv = mpg)
But "..." obscures the logic of the function, and it could not be used in a custom function with 2 or more formal arguments requiring multiple calling arguments.
Is there a way to write the above function to utilize a formal argument (e.g., "groups") rather than "..." that can receive a single vector name or a vector of vector names as its argument(s)? Something like:
foo <- function(data, groups, dv){
groups <- enquos(groups)
dv <- enquo(dv)
data %>%
group_by(!!!groups) %>%
summarise(group_mean = mean(!!dv))
}
# Failing code
mtcars %>% foo(groups = c(vs, am), dv = mpg)
Note that this code would work, but require user to remember to use quos() in function body:
foo <- function(data, groups, dv){
dv <- enquo(dv)
data %>%
group_by(!!!groups) %>%
summarise(group_mean = mean(!!dv))
}
mtcars %>% foo(groups = quos(vs, am), dv = mpg)
I'd love to rely on enquos() in body of function instead.
回答1:
We could place the ...
at the end
foo <- function(data, dv, ...){
groups <- enquos(...)
dv <- enquo(dv)
data %>%
group_by(!!!groups) %>%
summarise(group_mean = mean(!!dv))
}
If we want to pass a vector
of 'group', then one option is group_by_at
foo <- function(data, groups, dv){
dv <- enquo(dv)
data %>%
group_by_at(vars(groups)) %>%
summarise(group_mean = mean(!!dv))
}
mtcars %>%
foo(groups = c("vs", "am"), dv = mpg)
# A tibble: 4 x 3
# Groups: vs [?]
# vs am group_mean
# <dbl> <dbl> <dbl>
#1 0 0 15.0
#2 0 1 19.8
#3 1 0 20.7
#4 1 1 28.4
One option if we want to pass unquoted expression with c
would be to convert it to expression and then evaluate it
foo <- function(data, groups, dv){
groups <- as.list(rlang::enexpr(groups))[-1]
dv <- enquo(dv)
data %>%
group_by(!!! groups) %>%
summarise(group_mean = mean(!!dv))
}
mtcars %>%
foo(groups = c(vs, am), dv = mpg)
# A tibble: 4 x 3
# Groups: vs [?]
# vs am group_mean
# <dbl> <dbl> <dbl>
#1 0 0 15.0
#2 0 1 19.8
#3 1 0 20.7
#4 1 1 28.4
Or as @Joe mentioned in the comments, enquo
should also work with group_by_at
foo <- function(data, groups, dv){
dv <- enquo(dv)
groups <- enquos(groups)
data %>%
group_by_at(vars(!!!groups)) %>%
summarise(group_mean = mean(!!dv))
}
mtcars %>%
foo(groups = c(vs, am), dv = mpg)
# A tibble: 4 x 3
# Groups: vs [?]
# vs am group_mean
# <dbl> <dbl> <dbl>
#1 0 0 15.0
#2 0 1 19.8
#3 1 0 20.7
#4 1 1 28.4
来源:https://stackoverflow.com/questions/53160709/pass-multiple-calling-arguments-to-a-formal-argument-in-dplyr-custom-function-wi