之间的区别!和mutate_at

时间:2019-02-22 01:51:55

标签: r dplyr mutate rlang tidyeval

我一直在研究a number of sourcestidyeval语义,但得到的结果我无法解释。 / p>

我正在使用mutate_atcase_when来变换某些变量,方法是:(1)使用引号检索其名称,(2)使用gsub修改其名称,以及(3)引用与修改后的名称关联的数据。

在我的最小示例中,我将foo$c创建为foo$b的转换,这意味着仅使用foo$a中的值。步骤(1)和(2)似乎很简单:

library(tidyverse)
library(rlang)

foo <- data.frame(a = 1, b = 2)

foo %>%
  mutate_at(vars(c = b),
            funs(case_when(
              TRUE ~ gsub("b", "a", as_name(quo(.)))
            )))
#>   a b c
#> 1 1 2 a

foo$c包含我们要查看的变量的正确名称。我了解我需要使用symbol将字符串转换为sym(),然后对其求值。如果我使用简单的mutate()!!sym()可以正常工作:

foo %>%
  mutate(c := !!sym(gsub("b", "a", as_name(quo(b)))))
#>   a b c
#> 1 1 2 1

但是当我在mutate_at(case_when())内执行此操作时,我没有得到正确的结果:

foo %>%
  mutate_at(vars(c = b),
            funs(case_when(
              TRUE ~ !!sym(gsub("b", "a", as_name(quo(.))))
            )))
#>   a b c
#> 1 1 2 2

要查看发生了什么,我做了一个简单的打印功能。如果没有!!,则从打印输出看,好像gsub()sym()都能产生预期的结果:

look <- function(x) {
  print(x)
  print(typeof(x))
  return(x)
}

foo %>%
  mutate_at(vars(c = b),
            funs(case_when(
              TRUE ~ look(sym(look(gsub("b", "a", as_name(quo(.))))))
            )))
#> [1] "a"
#> [1] "character"
#> a
#> [1] "symbol"
#> Error in mutate_impl(.data, dots): Evaluation error: object of type 'symbol' is not subsettable.

一旦我将!!放在前面,打印输出似乎表明gsub()sym()的结果有所不同:

foo %>%
  mutate_at(vars(c = b),
            funs(case_when(
              TRUE ~ !!(look(sym(look(gsub("b", "a", as_name(quo(.)))))))
            )))
#> [1] "."
#> [1] "character"
#> .
#> [1] "symbol"
#> [1] "."
#> [1] "character"
#> .
#> [1] "symbol"
#>   a b c
#> 1 1 2 2

我不明白添加!!会如何改变嵌套sym(gsub())的结果。最后添加新操作不应更改先前/内部的结果。我已经读过!!是“不是函数调用,而是语法操作”,但我不完全欣赏这种区别或这种区别如何改变结果。

使用eval_tidy代替!!似乎可以正常工作,尽管我无法解释原因:

foo %>%
  mutate_at(vars(c = b),
            funs(case_when(
              TRUE ~ eval_tidy(look(sym(look(gsub("b", "a", as_name(quo(.)))))))
            )))
#> [1] "a"
#> [1] "character"
#> a
#> [1] "symbol"
#>   a b c
#> 1 1 2 1

1 个答案:

答案 0 :(得分:1)

仅几条评论:

(a)范围动词目前可以用.代词的怪异替换来工作。我们正在努力使用应该以更原则性的方式工作的功能(或功能清单)。我建议使用函数或purrr lambdas代替funs(),这样可以消除一些怪异之处。

(b)!!运算符与计时有关。对于您而言,是funs()在替换发生之前立即对其进行处理。

(c)使用范围变体时,最好忘记整洁的评估,并从映射函数的角度进行思考。 这意味着您无权访问当前正在映射的列的名称。我们将来可能会将此功能添加为功能,但目前最好避免这种情况。< / p>

有关最近的相关讨论,另请参见https://github.com/tidyverse/dplyr/issues/4199