将 rlang::exec 与使用 rlang::ensym 的函数一起使用
using `rlang::exec` with functions that use `rlang::ensym`
我正在尝试编写一个有点复杂的自定义函数,因此为了简单起见,我创建了玩具示例。
假设我想写一个函数-
- 自动决定合适的功能给运行:例如,
t 检验或方差分析。
- 接受
"quoted"
和 unquoted
参数
所以我写了一个函数来 运行 t 检验(按预期工作):
set.seed(123)
library(rlang)
library(tidyverse)
# t-test function
fun_t <- function(data, x, y) {
# make sure both quoted and unquoted arguments work
x <- rlang::ensym(x)
y <- rlang::ensym(y)
# t-test
broom::tidy(stats::t.test(
formula = rlang::new_formula({{ y }}, {{ x }}),
data = data
))
}
# works fine
fun_t(mtcars, am, wt)
#> # A tibble: 1 x 10
#> estimate estimate1 estimate2 statistic p.value parameter conf.low
#> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 1.36 3.77 2.41 5.49 6.27e-6 29.2 0.853
#> # ... with 3 more variables: conf.high <dbl>, method <chr>,
#> # alternative <chr>
fun_t(mtcars, "am", "wt")
#> # A tibble: 1 x 10
#> estimate estimate1 estimate2 statistic p.value parameter conf.low
#> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 1.36 3.77 2.41 5.49 6.27e-6 29.2 0.853
#> # ... with 3 more variables: conf.high <dbl>, method <chr>,
#> # alternative <chr>
然后我写了一个函数到 运行 方差分析(按预期工作):
# anova function
fun_anova <- function(data, x, y) {
# make sure both quoted and unquoted arguments work
x <- rlang::ensym(x)
y <- rlang::ensym(y)
# t-test
broom::tidy(stats::aov(
formula = rlang::new_formula({{ y }}, {{ x }}),
data = data
))
}
# works fine
fun_anova(mtcars, cyl, wt)
#> # A tibble: 2 x 6
#> term df sumsq meansq statistic p.value
#> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 cyl 1 18.2 18.2 47.4 0.000000122
#> 2 Residuals 30 11.5 0.384 NA NA
fun_anova(mtcars, "cyl", "wt")
#> # A tibble: 2 x 6
#> term df sumsq meansq statistic p.value
#> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 cyl 1 18.2 18.2 47.4 0.000000122
#> 2 Residuals 30 11.5 0.384 NA NA
然后我写一个元函数从上面选择合适的函数-
fun_meta <- function(data, x, y) {
# make sure both quoted and unquoted arguments work
x <- rlang::ensym(x)
y <- rlang::ensym(y)
# which test to run?
if (nlevels(data %>% dplyr::pull({{ x }})) == 2L) {
.f <- fun_t
} else {
.f <- fun_anova
}
# executing the appropriate function
rlang::exec(
.fn = .f,
data = data,
x = x,
y = y
)
}
# using the meta-function
fun_meta(mtcars, am, wt)
#> Only strings can be converted to symbols
fun_meta(mtcars, "am", "wt")
#> Only strings can be converted to symbols
但这似乎不起作用。关于我在这里做错了什么以及如何让它发挥作用有什么想法吗?
问题似乎源于通过元函数中的 rlang::exec()
将相当于 x = rlang::ensym(am)
的内容传递给您的各个函数。
ensym()
函数只接受字符串或符号,所以这样做会导致出现错误消息。鉴于此,将您的 x
和 y
参数转换为字符串应该会有所帮助。
所以元函数可以是:
fun_meta <- function(data, x, y) {
# make sure both quoted and unquoted arguments work
x <- rlang::ensym(x)
y <- rlang::ensym(y)
# which test to run?
if (dplyr::n_distinct(data %>% dplyr::pull({{ x }})) == 2L) {
.f <- fun_t
} else {
.f <- fun_anova
}
# executing the appropriate function
rlang::exec(
.fn = .f,
data = data,
x = rlang::as_string(x),
y = rlang::as_string(y)
)
}
(我从 nlevels
切换到 n_distinct()
因为 am
和 cyl
不是因素,所以我没有得到正确的结果来与你的比较原始结果。)
现在可以同时使用裸符号和字符串了:
fun_meta(mtcars, am, wt)
# A tibble: 1 x 10
estimate estimate1 estimate2 statistic p.value parameter conf.low conf.high
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 1.36 3.77 2.41 5.49 6.27e-6 29.2 0.853 1.86
# ... with 2 more variables: method <chr>, alternative <chr>
> fun_meta(mtcars, "am", "wt")
fun_meta(mtcars, "am", "wt")
# A tibble: 1 x 10
estimate estimate1 estimate2 statistic p.value parameter conf.low conf.high
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 1.36 3.77 2.41 5.49 6.27e-6 29.2 0.853 1.86
# ... with 2 more variables: method <chr>, alternative <chr>
我正在尝试编写一个有点复杂的自定义函数,因此为了简单起见,我创建了玩具示例。
假设我想写一个函数-
- 自动决定合适的功能给运行:例如, t 检验或方差分析。
- 接受
"quoted"
和unquoted
参数
所以我写了一个函数来 运行 t 检验(按预期工作):
set.seed(123)
library(rlang)
library(tidyverse)
# t-test function
fun_t <- function(data, x, y) {
# make sure both quoted and unquoted arguments work
x <- rlang::ensym(x)
y <- rlang::ensym(y)
# t-test
broom::tidy(stats::t.test(
formula = rlang::new_formula({{ y }}, {{ x }}),
data = data
))
}
# works fine
fun_t(mtcars, am, wt)
#> # A tibble: 1 x 10
#> estimate estimate1 estimate2 statistic p.value parameter conf.low
#> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 1.36 3.77 2.41 5.49 6.27e-6 29.2 0.853
#> # ... with 3 more variables: conf.high <dbl>, method <chr>,
#> # alternative <chr>
fun_t(mtcars, "am", "wt")
#> # A tibble: 1 x 10
#> estimate estimate1 estimate2 statistic p.value parameter conf.low
#> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 1.36 3.77 2.41 5.49 6.27e-6 29.2 0.853
#> # ... with 3 more variables: conf.high <dbl>, method <chr>,
#> # alternative <chr>
然后我写了一个函数到 运行 方差分析(按预期工作):
# anova function
fun_anova <- function(data, x, y) {
# make sure both quoted and unquoted arguments work
x <- rlang::ensym(x)
y <- rlang::ensym(y)
# t-test
broom::tidy(stats::aov(
formula = rlang::new_formula({{ y }}, {{ x }}),
data = data
))
}
# works fine
fun_anova(mtcars, cyl, wt)
#> # A tibble: 2 x 6
#> term df sumsq meansq statistic p.value
#> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 cyl 1 18.2 18.2 47.4 0.000000122
#> 2 Residuals 30 11.5 0.384 NA NA
fun_anova(mtcars, "cyl", "wt")
#> # A tibble: 2 x 6
#> term df sumsq meansq statistic p.value
#> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 cyl 1 18.2 18.2 47.4 0.000000122
#> 2 Residuals 30 11.5 0.384 NA NA
然后我写一个元函数从上面选择合适的函数-
fun_meta <- function(data, x, y) {
# make sure both quoted and unquoted arguments work
x <- rlang::ensym(x)
y <- rlang::ensym(y)
# which test to run?
if (nlevels(data %>% dplyr::pull({{ x }})) == 2L) {
.f <- fun_t
} else {
.f <- fun_anova
}
# executing the appropriate function
rlang::exec(
.fn = .f,
data = data,
x = x,
y = y
)
}
# using the meta-function
fun_meta(mtcars, am, wt)
#> Only strings can be converted to symbols
fun_meta(mtcars, "am", "wt")
#> Only strings can be converted to symbols
但这似乎不起作用。关于我在这里做错了什么以及如何让它发挥作用有什么想法吗?
问题似乎源于通过元函数中的 rlang::exec()
将相当于 x = rlang::ensym(am)
的内容传递给您的各个函数。
ensym()
函数只接受字符串或符号,所以这样做会导致出现错误消息。鉴于此,将您的 x
和 y
参数转换为字符串应该会有所帮助。
所以元函数可以是:
fun_meta <- function(data, x, y) {
# make sure both quoted and unquoted arguments work
x <- rlang::ensym(x)
y <- rlang::ensym(y)
# which test to run?
if (dplyr::n_distinct(data %>% dplyr::pull({{ x }})) == 2L) {
.f <- fun_t
} else {
.f <- fun_anova
}
# executing the appropriate function
rlang::exec(
.fn = .f,
data = data,
x = rlang::as_string(x),
y = rlang::as_string(y)
)
}
(我从 nlevels
切换到 n_distinct()
因为 am
和 cyl
不是因素,所以我没有得到正确的结果来与你的比较原始结果。)
现在可以同时使用裸符号和字符串了:
fun_meta(mtcars, am, wt)
# A tibble: 1 x 10
estimate estimate1 estimate2 statistic p.value parameter conf.low conf.high
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 1.36 3.77 2.41 5.49 6.27e-6 29.2 0.853 1.86
# ... with 2 more variables: method <chr>, alternative <chr>
> fun_meta(mtcars, "am", "wt")
fun_meta(mtcars, "am", "wt")
# A tibble: 1 x 10
estimate estimate1 estimate2 statistic p.value parameter conf.low conf.high
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 1.36 3.77 2.41 5.49 6.27e-6 29.2 0.853 1.86
# ... with 2 more variables: method <chr>, alternative <chr>