使用 rlang 允许参数列表,然后在自定义函数中使用它
using rlang to allow a list of arguments and then use it in custom function
我正在编写一个自定义函数,我希望其中一个参数采用变量列表。我已经设法使用 rlang
和对 ...
的一些基本理解来正确阅读函数中的这个列表。但我不知道如何将此列表作为参数分配给另一个函数(如 dplyr::group_by
)。我在下面是完全可重现的示例以及我想要的最终结果。
# loading the needed libraries
library(dplyr)
library(rlang)
library(datasets)
# defining the custom function
prac.fn <- function(data, vars = ..., measure) {
# getting the dataframe ready
df <-
dplyr::select(.data = data,
!!rlang::enquo(vars),
!!rlang::enquo(measure))
# print to see if all variables are included
print(head(df))
# summarize by specified grouping variables
df %>%
dplyr::group_by(.data = ., c(!!rlang::enquo(vars))) %>%
dplyr::summarise(mean = mean(!!rlang::enquo(measure)))
}
# use the function (doesn't work)
prac.fn(data = mtcars,
vars = c(cyl, am),
measure = wt)
#> cyl am wt
#> Mazda RX4 6 1 2.620
#> Mazda RX4 Wag 6 1 2.875
#> Datsun 710 4 1 2.320
#> Hornet 4 Drive 6 0 3.215
#> Hornet Sportabout 8 0 3.440
#> Valiant 6 0 3.460
#> Error in mutate_impl(.data, dots): Column `c(c(cyl, am))` must be length 32 (the number of rows) or one, not 64
# output I want
mtcars %>%
dplyr::group_by(cyl, am) %>%
dplyr::summarise(mean = mean(wt))
#> # A tibble: 6 x 3
#> # Groups: cyl [?]
#> cyl am mean
#> <dbl> <dbl> <dbl>
#> 1 4.00 0 2.94
#> 2 4.00 1.00 2.04
#> 3 6.00 0 3.39
#> 4 6.00 1.00 2.76
#> 5 8.00 0 4.10
#> 6 8.00 1.00 3.37
由 reprex package (v0.2.0) 创建于 2018-02-17。
在group_by
中,将'vars'转换为quosure(enquo
)后,用quo_squash
压平表达式,将其转换为list
(as.list
) 并删除第一个元素,即。 c
,然后用!!!
求值
prac.fn <- function(data, vars, measure) {
data %>%
select(!!rlang::enquo(vars),
!!rlang::enquo(measure)) %>%
dplyr::group_by(!!! as.list(quo_squash(rlang::enquo(vars)))[-1]) %>%
dplyr::summarise(mean = mean(!!rlang::enquo(measure)))
}
-测试
prac.fn(data = mtcars,
vars = c(cyl, am),
measure = wt)
# A tibble: 6 x 3
# Groups: cyl [?]
# cyl am mean
# <dbl> <dbl> <dbl>
#1 4.00 0 2.94
#2 4.00 1.00 2.04
#3 6.00 0 3.39
#4 6.00 1.00 2.76
#5 8.00 0 4.10
#6 8.00 1.00 3.37
检查更多组数
prac.fn(data = mtcars,
vars = c(cyl, am, gear),
measure = wt)
# A tibble: 10 x 4
# Groups: cyl, am [?]
# cyl am gear mean
# <dbl> <dbl> <dbl> <dbl>
# 1 4.00 0 3.00 2.46
# 2 4.00 0 4.00 3.17
# 3 4.00 1.00 4.00 2.11
# 4 4.00 1.00 5.00 1.83
# 5 6.00 0 3.00 3.34
# 6 6.00 0 4.00 3.44
# 7 6.00 1.00 4.00 2.75
# 8 6.00 1.00 5.00 2.77
# 9 8.00 0 3.00 4.10
#10 8.00 1.00 5.00 3.37
不清楚 OP 是否总是想对 vars
参数使用 c()
即,如果有单个分组变量,如果传递参数的行为相同,则该函数有效
prac.fn(data = mtcars,
vars = c(cyl),
measure = wt)
#<quosure>
# expr: ^c(cyl)
# env: global
# A tibble: 3 x 2
# cyl mean
# <dbl> <dbl>
#1 4.00 2.29
#2 6.00 3.12
#3 8.00 4.00
但是,如果我们必须更改行为,即没有 c()
的 vars = cyl
,则需要使用 if/else
语句来解决,即
prac.fnN <- function(data, vars, measure) {
vars <- as.list(quo_squash(enquo(vars)))
vars <- if(length(vars) ==1) vars else vars[-1]
data %>%
select(!!! vars,
!!rlang::enquo(measure)) %>%
dplyr::group_by(!!! vars) %>%
dplyr::summarise(mean = mean(!!rlang::enquo(measure)))
}
-测试
prac.fnN(data = mtcars,
vars = cyl,
measure = wt)
# A tibble: 3 x 2
# cyl mean
# <dbl> <dbl>
#1 4.00 2.29
#2 6.00 3.12
#3 8.00 4.00
prac.fnN(data = mtcars,
vars = c(cyl),
measure = wt)
# A tibble: 3 x 2
# cyl mean
# <dbl> <dbl>
#1 4.00 2.29
#2 6.00 3.12
#3 8.00 4.00
prac.fnN(data = mtcars,
vars = c(cyl, am),
measure = wt)
# A tibble: 6 x 3
# Groups: cyl [?]
# cyl am mean
# <dbl> <dbl> <dbl>
#1 4.00 0 2.94
#2 4.00 1.00 2.04
#3 6.00 0 3.39
#4 6.00 1.00 2.76
#5 8.00 0 4.10
#6 8.00 1.00 3.37
除了上述方法之外,自然的选择是将参数作为quos/quo
传递,然后我们就不必考虑enquo
和其他if/else
prac.fnQ <- function(data, vars, measure) {
stopifnot(is_quosures(vars))
stopifnot(is_quosure(measure))
data %>%
select(!!! vars, !! measure) %>%
dplyr::group_by(!!! vars) %>%
dplyr::summarise(mean = mean(!! measure))
}
-测试
prac.fnQ(data = mtcars,
vars = quos(cyl, am),
measure = quo(wt))
# A tibble: 6 x 3
# Groups: cyl [?]
# cyl am mean
# <dbl> <dbl> <dbl>
#1 4.00 0 2.94
#2 4.00 1.00 2.04
#3 6.00 0 3.39
#4 6.00 1.00 2.76
#5 8.00 0 4.10
#6 8.00 1.00 3.37
如果我们还需要检查'measure'个变量(假设我们有多个'measure'个变量)是否是numeric
prac.fnQn <- function(data, vars, measure) {
stopifnot(is_quosures(vars))
stopifnot(is_quosures(measure))
data %>%
select(!!! vars, !!! measure) %>%
dplyr::group_by(!!! vars) %>%
summarise_if(is.numeric, mean)
}
prac.fnQn(data = mtcars,
vars = quos(cyl, am),
measure = quos(wt))
# A tibble: 6 x 3
# Groups: cyl [?]
# cyl am wt
# <dbl> <dbl> <dbl>
#1 4.00 0 2.94
#2 4.00 1.00 2.04
#3 6.00 0 3.39
#4 6.00 1.00 2.76
#5 8.00 0 4.10
#6 8.00 1.00 3.37
我正在编写一个自定义函数,我希望其中一个参数采用变量列表。我已经设法使用 rlang
和对 ...
的一些基本理解来正确阅读函数中的这个列表。但我不知道如何将此列表作为参数分配给另一个函数(如 dplyr::group_by
)。我在下面是完全可重现的示例以及我想要的最终结果。
# loading the needed libraries
library(dplyr)
library(rlang)
library(datasets)
# defining the custom function
prac.fn <- function(data, vars = ..., measure) {
# getting the dataframe ready
df <-
dplyr::select(.data = data,
!!rlang::enquo(vars),
!!rlang::enquo(measure))
# print to see if all variables are included
print(head(df))
# summarize by specified grouping variables
df %>%
dplyr::group_by(.data = ., c(!!rlang::enquo(vars))) %>%
dplyr::summarise(mean = mean(!!rlang::enquo(measure)))
}
# use the function (doesn't work)
prac.fn(data = mtcars,
vars = c(cyl, am),
measure = wt)
#> cyl am wt
#> Mazda RX4 6 1 2.620
#> Mazda RX4 Wag 6 1 2.875
#> Datsun 710 4 1 2.320
#> Hornet 4 Drive 6 0 3.215
#> Hornet Sportabout 8 0 3.440
#> Valiant 6 0 3.460
#> Error in mutate_impl(.data, dots): Column `c(c(cyl, am))` must be length 32 (the number of rows) or one, not 64
# output I want
mtcars %>%
dplyr::group_by(cyl, am) %>%
dplyr::summarise(mean = mean(wt))
#> # A tibble: 6 x 3
#> # Groups: cyl [?]
#> cyl am mean
#> <dbl> <dbl> <dbl>
#> 1 4.00 0 2.94
#> 2 4.00 1.00 2.04
#> 3 6.00 0 3.39
#> 4 6.00 1.00 2.76
#> 5 8.00 0 4.10
#> 6 8.00 1.00 3.37
由 reprex package (v0.2.0) 创建于 2018-02-17。
在group_by
中,将'vars'转换为quosure(enquo
)后,用quo_squash
压平表达式,将其转换为list
(as.list
) 并删除第一个元素,即。 c
,然后用!!!
求值
prac.fn <- function(data, vars, measure) {
data %>%
select(!!rlang::enquo(vars),
!!rlang::enquo(measure)) %>%
dplyr::group_by(!!! as.list(quo_squash(rlang::enquo(vars)))[-1]) %>%
dplyr::summarise(mean = mean(!!rlang::enquo(measure)))
}
-测试
prac.fn(data = mtcars,
vars = c(cyl, am),
measure = wt)
# A tibble: 6 x 3
# Groups: cyl [?]
# cyl am mean
# <dbl> <dbl> <dbl>
#1 4.00 0 2.94
#2 4.00 1.00 2.04
#3 6.00 0 3.39
#4 6.00 1.00 2.76
#5 8.00 0 4.10
#6 8.00 1.00 3.37
检查更多组数
prac.fn(data = mtcars,
vars = c(cyl, am, gear),
measure = wt)
# A tibble: 10 x 4
# Groups: cyl, am [?]
# cyl am gear mean
# <dbl> <dbl> <dbl> <dbl>
# 1 4.00 0 3.00 2.46
# 2 4.00 0 4.00 3.17
# 3 4.00 1.00 4.00 2.11
# 4 4.00 1.00 5.00 1.83
# 5 6.00 0 3.00 3.34
# 6 6.00 0 4.00 3.44
# 7 6.00 1.00 4.00 2.75
# 8 6.00 1.00 5.00 2.77
# 9 8.00 0 3.00 4.10
#10 8.00 1.00 5.00 3.37
不清楚 OP 是否总是想对 vars
参数使用 c()
即,如果有单个分组变量,如果传递参数的行为相同,则该函数有效
prac.fn(data = mtcars,
vars = c(cyl),
measure = wt)
#<quosure>
# expr: ^c(cyl)
# env: global
# A tibble: 3 x 2
# cyl mean
# <dbl> <dbl>
#1 4.00 2.29
#2 6.00 3.12
#3 8.00 4.00
但是,如果我们必须更改行为,即没有 c()
的 vars = cyl
,则需要使用 if/else
语句来解决,即
prac.fnN <- function(data, vars, measure) {
vars <- as.list(quo_squash(enquo(vars)))
vars <- if(length(vars) ==1) vars else vars[-1]
data %>%
select(!!! vars,
!!rlang::enquo(measure)) %>%
dplyr::group_by(!!! vars) %>%
dplyr::summarise(mean = mean(!!rlang::enquo(measure)))
}
-测试
prac.fnN(data = mtcars,
vars = cyl,
measure = wt)
# A tibble: 3 x 2
# cyl mean
# <dbl> <dbl>
#1 4.00 2.29
#2 6.00 3.12
#3 8.00 4.00
prac.fnN(data = mtcars,
vars = c(cyl),
measure = wt)
# A tibble: 3 x 2
# cyl mean
# <dbl> <dbl>
#1 4.00 2.29
#2 6.00 3.12
#3 8.00 4.00
prac.fnN(data = mtcars,
vars = c(cyl, am),
measure = wt)
# A tibble: 6 x 3
# Groups: cyl [?]
# cyl am mean
# <dbl> <dbl> <dbl>
#1 4.00 0 2.94
#2 4.00 1.00 2.04
#3 6.00 0 3.39
#4 6.00 1.00 2.76
#5 8.00 0 4.10
#6 8.00 1.00 3.37
除了上述方法之外,自然的选择是将参数作为quos/quo
传递,然后我们就不必考虑enquo
和其他if/else
prac.fnQ <- function(data, vars, measure) {
stopifnot(is_quosures(vars))
stopifnot(is_quosure(measure))
data %>%
select(!!! vars, !! measure) %>%
dplyr::group_by(!!! vars) %>%
dplyr::summarise(mean = mean(!! measure))
}
-测试
prac.fnQ(data = mtcars,
vars = quos(cyl, am),
measure = quo(wt))
# A tibble: 6 x 3
# Groups: cyl [?]
# cyl am mean
# <dbl> <dbl> <dbl>
#1 4.00 0 2.94
#2 4.00 1.00 2.04
#3 6.00 0 3.39
#4 6.00 1.00 2.76
#5 8.00 0 4.10
#6 8.00 1.00 3.37
如果我们还需要检查'measure'个变量(假设我们有多个'measure'个变量)是否是numeric
prac.fnQn <- function(data, vars, measure) {
stopifnot(is_quosures(vars))
stopifnot(is_quosures(measure))
data %>%
select(!!! vars, !!! measure) %>%
dplyr::group_by(!!! vars) %>%
summarise_if(is.numeric, mean)
}
prac.fnQn(data = mtcars,
vars = quos(cyl, am),
measure = quos(wt))
# A tibble: 6 x 3
# Groups: cyl [?]
# cyl am wt
# <dbl> <dbl> <dbl>
#1 4.00 0 2.94
#2 4.00 1.00 2.04
#3 6.00 0 3.39
#4 6.00 1.00 2.76
#5 8.00 0 4.10
#6 8.00 1.00 3.37