R 在不使用字符串操作的情况下将函数调用包装到另一个调用中
R wrap function call into another call without using string manipulation
我正在寻找一种将函数调用包装到另一个函数调用中的好方法。在调用其他函数之前包装一个函数很容易,但修改现有调用对我来说似乎不是那么直接。
我找到了一种使用下面的示例进行解释的方法,但它基本上依赖于将调用转换为列表转换为字符串,然后将新调用添加为字符串并使用 [=11 将所有内容转换回语言=].
有更简洁的方法吗?
假设我正在为 dplyr
构建自定义 summarise
函数,它将检查输出是否为向量,在这种情况下,使用 [= 创建一个 df-col
15=] 在现有呼叫上。
这是代表。
library(tidyverse)
short_sum <- function(data, ...) {
fns <- rlang::enquos(...)
fns <- purrr::map(fns, function(x) {
res <- rlang::eval_tidy(x, data = data)
if ((is.vector(res) || is.factor(res)) && length(res) > 1) {
# is there a better way to do this (start) ---
# get expression of call and turn it into a string
x_expr <- as.character(list(rlang::quo_get_expr(x)))
# construct a string with expression above wrapped in another call
x_expr <- paste0(
"pivot_wider(enframe(",
x_expr,
"), names_from = name, values_from = value)"
)
# turn string into language and replace expression in x
x <- rlang::quo_set_expr(x, str2lang(x_expr))
# is there a better way to do this (end) ---
x
} else {
x
}
})
dplyr::summarise(data, !!! fns, .groups="drop")
}
mtcars %>%
as_tibble %>%
short_sum(quant = quantile(mpg),
range = range(wt))
#> # A tibble: 1 x 2
#> quant$`0%` $`25%` $`50%` $`75%` $`100%` range$`1` $`2`
#> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 10.4 15.4 19.2 22.8 33.9 1.51 5.42
由 reprex package (v0.3.0)
于 2020-06-14 创建
这个问题是基于我的回答,我在其中使用了上面的方法。
我不是 100% 确定该函数的作用,但我认为您可能正在寻找这样的东西:
short_sum <- function(data, ...) {
fns <- rlang::enquos(...)
fns <- purrr::map(fns, function(x) {
res <- rlang::eval_tidy(x, data = data)
if ((is.vector(res) || is.factor(res)) && length(res) > 1) {
rlang::expr(pivot_wider(enframe(
!!rlang::quo_get_expr(x)), names_from = name, values_from = value))
# or
# bquote(pivot_wider(enframe(
# .(rlang::quo_get_expr(x))), names_from = name, values_from = value))
} else {
x
}
})
dplyr::summarise(data, !!! fns, .groups="drop")
}
expr()
类似于 quote()
,只是您可以使用 !!
或 !!!
取消对表达式部分的引用。
bquote()
也类似于 quote()
,但在基础 R 中,您使用 .()
取消引用。
我正在寻找一种将函数调用包装到另一个函数调用中的好方法。在调用其他函数之前包装一个函数很容易,但修改现有调用对我来说似乎不是那么直接。
我找到了一种使用下面的示例进行解释的方法,但它基本上依赖于将调用转换为列表转换为字符串,然后将新调用添加为字符串并使用 [=11 将所有内容转换回语言=].
有更简洁的方法吗?
假设我正在为 dplyr
构建自定义 summarise
函数,它将检查输出是否为向量,在这种情况下,使用 [= 创建一个 df-col
15=] 在现有呼叫上。
这是代表。
library(tidyverse)
short_sum <- function(data, ...) {
fns <- rlang::enquos(...)
fns <- purrr::map(fns, function(x) {
res <- rlang::eval_tidy(x, data = data)
if ((is.vector(res) || is.factor(res)) && length(res) > 1) {
# is there a better way to do this (start) ---
# get expression of call and turn it into a string
x_expr <- as.character(list(rlang::quo_get_expr(x)))
# construct a string with expression above wrapped in another call
x_expr <- paste0(
"pivot_wider(enframe(",
x_expr,
"), names_from = name, values_from = value)"
)
# turn string into language and replace expression in x
x <- rlang::quo_set_expr(x, str2lang(x_expr))
# is there a better way to do this (end) ---
x
} else {
x
}
})
dplyr::summarise(data, !!! fns, .groups="drop")
}
mtcars %>%
as_tibble %>%
short_sum(quant = quantile(mpg),
range = range(wt))
#> # A tibble: 1 x 2
#> quant$`0%` $`25%` $`50%` $`75%` $`100%` range$`1` $`2`
#> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 10.4 15.4 19.2 22.8 33.9 1.51 5.42
由 reprex package (v0.3.0)
于 2020-06-14 创建这个问题是基于我的回答
我不是 100% 确定该函数的作用,但我认为您可能正在寻找这样的东西:
short_sum <- function(data, ...) {
fns <- rlang::enquos(...)
fns <- purrr::map(fns, function(x) {
res <- rlang::eval_tidy(x, data = data)
if ((is.vector(res) || is.factor(res)) && length(res) > 1) {
rlang::expr(pivot_wider(enframe(
!!rlang::quo_get_expr(x)), names_from = name, values_from = value))
# or
# bquote(pivot_wider(enframe(
# .(rlang::quo_get_expr(x))), names_from = name, values_from = value))
} else {
x
}
})
dplyr::summarise(data, !!! fns, .groups="drop")
}
expr()
类似于 quote()
,只是您可以使用 !!
或 !!!
取消对表达式部分的引用。
bquote()
也类似于 quote()
,但在基础 R 中,您使用 .()
取消引用。