通过整洁的评估来改变变量(符号或字符串)
mutating variables (symbol or string) with tidy evaluation
我想在我的函数中使用 tidy 求值,同时改变输入的变量,但似乎无法使函数正常工作。
我都读过 and ,他们似乎建议需要 不同的解决方案 基于符号还是字符串作为争论。但我更喜欢一个适用于这两个输入的问题的通用解决方案。
有办法吗?
函数
library(magrittr)
foo <- function(data, x) {
# I know this is one solution to solve my problem
# But I want to avoid it because it creates problem if the function is to be used within a loop
# x <- rlang::ensym(x)
df <- tibble::as_tibble(dplyr::select(data, {{ x }}))
df %>% dplyr::mutate(.data = ., {{ x }} := droplevels(as.factor({{ x }})))
}
按预期工作
foo(mtcars, am)
#> # A tibble: 32 x 1
#> am
#> <fct>
#> 1 1
#> 2 1
#> 3 1
#> 4 0
#> 5 0
#> 6 0
#> 7 0
#> 8 0
#> 9 0
#> 10 0
#> # ... with 22 more rows
没有按预期工作
foo(mtcars, "am")
#> # A tibble: 32 x 1
#> am
#> <fct>
#> 1 am
#> 2 am
#> 3 am
#> 4 am
#> 5 am
#> 6 am
#> 7 am
#> 8 am
#> 9 am
#> 10 am
#> # ... with 22 more rows
P.S。如果您对可能的解决方案在循环中引起的问题感到好奇,这里有一个代表:
library(magrittr)
col.name <- colnames(mtcars)
foo <- function(data, x) {
x <- rlang::ensym(x)
df <- tibble::as_tibble(dplyr::select(data, {{ x }}))
df %>% dplyr::mutate(.data = ., {{ x }} := droplevels(as.factor({{ x }})))
}
for (i in 3:length(mtcars)) {
foo(mtcars, col.name[i])
}
#> Error: Only strings can be converted to symbols
也许试试 !!
:
#Function
col.name <- colnames(mtcars)
foo <- function(data, x) {
x <- rlang::ensym(x)
df <- tibble::as_tibble(dplyr::select(data, {{ x }}))
df %>% dplyr::mutate(.data = ., {{ x }} := droplevels(as.factor({{ x }})))
}
#Loop
for (i in 3:length(mtcars)) {
print(foo(mtcars, !!col.name[i]))
}
如果您想与循环一起使用,该函数也适用于 foo(mtcars, !!'am')
和 foo(mtcars, am)
。
通常不建议尝试让您的函数与屏蔽列和列名兼容,因为这不是 tidyverse 函数设计的工作方式。
首先选择是要进行操作还是选择。如果选择后者,则可以使用 all_of()
等 tidyselect 功能来与字符向量兼容。在所有 dplyr 动词中进行选择的一种简单方法是使用 across()
.
在这种情况下,您也开始使用 select()
,因此我们可以只使用 across(everything())
。一般来说,你可以做 across({{ x }})
.
foo <- function(data, x) {
data %>%
as_tibble() %>%
select({{ x }}) %>%
# Could also remove the `select()` step and pass `{{ x }}` to across()
mutate(across(everything(), ~ droplevels(as.factor(.))))
}
foo(mtcars, am)
由于您已将 x
转发到选择上下文,您的函数现在支持所有 tidyselect 功能。如果需要传递字符向量或字符串,只需使用 all_of()
。调整您的示例:
nms <- names(mtcars)
for (i in 3:length(mtcars)) {
foo(mtcars, all_of(nms[i]))
}
我想在我的函数中使用 tidy 求值,同时改变输入的变量,但似乎无法使函数正常工作。
我都读过
有办法吗?
函数
library(magrittr)
foo <- function(data, x) {
# I know this is one solution to solve my problem
# But I want to avoid it because it creates problem if the function is to be used within a loop
# x <- rlang::ensym(x)
df <- tibble::as_tibble(dplyr::select(data, {{ x }}))
df %>% dplyr::mutate(.data = ., {{ x }} := droplevels(as.factor({{ x }})))
}
按预期工作
foo(mtcars, am)
#> # A tibble: 32 x 1
#> am
#> <fct>
#> 1 1
#> 2 1
#> 3 1
#> 4 0
#> 5 0
#> 6 0
#> 7 0
#> 8 0
#> 9 0
#> 10 0
#> # ... with 22 more rows
没有按预期工作
foo(mtcars, "am")
#> # A tibble: 32 x 1
#> am
#> <fct>
#> 1 am
#> 2 am
#> 3 am
#> 4 am
#> 5 am
#> 6 am
#> 7 am
#> 8 am
#> 9 am
#> 10 am
#> # ... with 22 more rows
P.S。如果您对可能的解决方案在循环中引起的问题感到好奇,这里有一个代表:
library(magrittr)
col.name <- colnames(mtcars)
foo <- function(data, x) {
x <- rlang::ensym(x)
df <- tibble::as_tibble(dplyr::select(data, {{ x }}))
df %>% dplyr::mutate(.data = ., {{ x }} := droplevels(as.factor({{ x }})))
}
for (i in 3:length(mtcars)) {
foo(mtcars, col.name[i])
}
#> Error: Only strings can be converted to symbols
也许试试 !!
:
#Function
col.name <- colnames(mtcars)
foo <- function(data, x) {
x <- rlang::ensym(x)
df <- tibble::as_tibble(dplyr::select(data, {{ x }}))
df %>% dplyr::mutate(.data = ., {{ x }} := droplevels(as.factor({{ x }})))
}
#Loop
for (i in 3:length(mtcars)) {
print(foo(mtcars, !!col.name[i]))
}
如果您想与循环一起使用,该函数也适用于 foo(mtcars, !!'am')
和 foo(mtcars, am)
。
通常不建议尝试让您的函数与屏蔽列和列名兼容,因为这不是 tidyverse 函数设计的工作方式。
首先选择是要进行操作还是选择。如果选择后者,则可以使用 all_of()
等 tidyselect 功能来与字符向量兼容。在所有 dplyr 动词中进行选择的一种简单方法是使用 across()
.
在这种情况下,您也开始使用 select()
,因此我们可以只使用 across(everything())
。一般来说,你可以做 across({{ x }})
.
foo <- function(data, x) {
data %>%
as_tibble() %>%
select({{ x }}) %>%
# Could also remove the `select()` step and pass `{{ x }}` to across()
mutate(across(everything(), ~ droplevels(as.factor(.))))
}
foo(mtcars, am)
由于您已将 x
转发到选择上下文,您的函数现在支持所有 tidyselect 功能。如果需要传递字符向量或字符串,只需使用 all_of()
。调整您的示例:
nms <- names(mtcars)
for (i in 3:length(mtcars)) {
foo(mtcars, all_of(nms[i]))
}