首页 > 解决方案 > 具有整洁评估的变异变量(符号或字符串)

问题描述

我想在我的函数中使用整洁的评估,同时改变输入的变量,但似乎无法让函数正常工作。

我已经阅读了tidy eval 与例如 mtcars %>% mutate(target := log(target))Tidy Evaluation not working with mutate 和 stringr,他们似乎建议需要根据符号是否有不同的解决方案或者一个字符串作为参数传递。但我更喜欢对这两个输入都适用的这个问题的通用解决方案。

有没有办法做到这一点?

功能

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

PS如果您对可能的解决方案在循环中引起的问题感到好奇,这里有一个代表:

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

标签: rdplyrtidyeval

解决方案


通常不建议尝试使您的函数与屏蔽列和列名兼容,因为这不是 tidyverse 函数的设计方式。

首先选择是否要采取行动或选择。如果您采用后者,那么您可以使用 tidyselect 功能,例如all_of()与字符向量兼容。在所有 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]))
}

推荐阅读