【问题标题】:Non-standard Evaluation using tidyr::expand使用 tidyr::expand 进行非标准评估
【发布时间】:2020-01-23 22:27:15
【问题描述】:

我在使用 tidyr 包运行非标准评估 (nse) 表达式时遇到问题。

基本上,我想做的是扩展可能相同或不同的两列,以实现具有所有可能组合的数据框。问题是这将是一个函数,所以我不会提前知道列名。

这是一个最小的例子:

library(tidyr)

dummy <- data.frame(x = c("ex1", "ex2"), y = c('cat1', 'cat2')) # dataset

tidyr::expand(dummy, x, y) # using standard evaluation works
tidyr::expand_(dummy, c("x", "y"))  # using the deprecated syntax works

# The following did not work:

  tidyr::expand(dummy, one_of('x'), y) # using select syntax
  tidyr::expand(dummy, vars('x', 'y')) # mutate_at style
  tidyr::expand(dummy, .data[[cnae_agg]], .data[[cnae_agg]])  # mutate current style  
  tidyr::expand(dummy, sym('x'), sym('y')) # trying to convert to symbols
  tidyr::expand(dummy, !!!enquos('x', 'y')) 
  tidyr::expand(dummy, !!('x'), y) # unquosure just one element
  tidyr::expand(dummy, !!!c("x", "y")) # unquosure vector of strings
  tidyr::expand(dummy, !!!c(quo("x"), quo("y"))) # unquosure vector that is being quosured before

所以,我有两个问题:

1) 使用 tidyr 扩展函数的正确语法是什么?

2) 我可能已经多次阅读Advanced R 关于准引用的章节,但我仍然不清楚为什么有几种不同的“风格”可以在 tidyverse 中使用nse,以及在哪里使用每个.

我基本上可以抛出几乎任何东西来选择/总结它会起作用,但是当使用 mutate 时,事情的反应会有所不同。

例如:

  # mutate
  mutate(dummy, new_var = .data[['x']]) # mutate basic style
  mutate(dummy, new_var = !!'x') # this just attributes 'x' to all rows


  # mutate at
  mutate_at(dummy, .vars=vars('y'), list(~'a')) # this works
  mutate_at(dummy, .vars=vars(!!'y'), list(~'a')) # this also works
  mutate_at(dummy, .vars=vars('y'), list(~`<-`(.,!!'x'))) # if we try to use unquote to create an attribution it does not work
  mutate_at(dummy, .vars=vars('y'), list(~`<-`(.,vars(!!'x')))) # even using vars, which works for variable selection, doesnt suffice

  # select 
  select(dummy, x) # this works
  select(dummy, 'x') # this works
  select_at(dummy, vars(!!'x')) # this works
  select_at(dummy, 'x') # this works
  select_at(dummy, !!'x') # this doesnt work

这让我想到了我的 2) 问题。

是否有更新的指南,其中包含 tidyverse 样式的所有当前语法,重点关注每个“动词”的用法差异,例如在“变异”与“选择”中(即当一个工作而另一个工作时)没有)?

以及如何知道我是否必须在其他 tidyverse 包(例如 tidyr)中使用 mutate 或 nse 的 select 样式?

【问题讨论】:

  • 不清楚mutate_at(dummy, .vars=vars('y'), list(~(.,!!'x')))这里的逻辑。您正在选择“y”列,然后它是否分配给不同的列?在这种情况下,您可以在单独的步骤中使用 renamerename_at
  • 我同意这样做肯定会更好。我只是强调一些您可以使用通常的 mutate 进行的操作,但在使用 nse 和不同的风格时会变得非常混乱。例如,使用 mutate 进行归因很容易:mutate(dummy, x = y),但使用 mutate_at 和 nse 似乎很难。

标签: r tidyr nse


【解决方案1】:

nse 上的更新指南是tidy evaluation guide。特别是,第 8 章介绍了它与dplyr 的关系以及一般模式。在您的情况下,有几种可能的模式,具体取决于您要向用户公开的内容。

模式1:简单的pass the dots to expand,让用户完全控制底层expand()

f <- function(...) {tidyr::expand(dummy, ...)}
f( x, y )    # End users specifies the columns via NSE

模式 2: 捕获每个变量的用户输入,并使用 new "curly curly" operator 将其传递给 expand()

g <- function( var1, var2 ) {tidyr::expand(dummy, {{var1}}, {{var2}})}
g( x, y )    # Once again, NSE, but the number of arguments is controlled

模式 3: 允许用户提供参数作为变量名或字符串。使用rlang::ensyms 将字符串转换为变量名:

h <- function(...) {tidyr::expand(dummy, !!!rlang::ensyms(...))}

# The interface now works with strings or NSE
h( "x", "y" )
h( x, y )

模式 3b:如果您想禁用 NSE 支持,并强制用户仅以字符串形式提供参数,那么对上述模式进行小幅修改将仅接受字符串:

h2 <- function(...) {tidyr::expand(dummy, !!!rlang::syms(list(...)))}
h2( "x", "y" )    # Strings OK
h2( x, y )        # Error: object 'x' not found

请注意,NSE 函数需要 quasiquotation 来处理存储在外部变量中的符号:

# Handling strings in external variables
str_name <- "x"
h( !!str_name, "y" )
h2( str_name, "y" )    # h2 doesn't support NSE; no !! needed

# Handling variable names as unevaluated expressions (NOT strings)
var_name <- quote(y)
f( x, !!var_name )
g( x, !!var_name )
h( x, !!var_name )

# Handling lists of variable names using !!! unquote-splice
# Works with functions that accept dots
arg_names <- rlang::exprs( x, y )
f( !!!arg_names )
h( !!!arg_names )

【讨论】:

  • +1 获取关于 tidyevaluation 的链接。感谢您的回答阿尔特姆。然而,我发现了一些奇怪的东西。如果我首先尝试将变量名称传递给字符串,例如str_name &lt;- "x",那么您展示的所有方法似乎都不起作用。但是,如果我这样做 ` i
  • 这是因为该函数正在寻找名为str_name 的列。使用!! 运算符告诉函数查找存储在inside str_name 的列名。请查看我的编辑。
  • @Elijah:添加了“仅字符串”模式,以防万一。
  • 谢谢,我现在看到了我之前缺少的东西!
【解决方案2】:

我们需要评估 (!!) symbols

tidyr::expand(dummy,  !!! syms(c('x', 'y')))
# A tibble: 4 x 2
#  x     y    
#  <fct> <fct>
#1 ex1   cat1 
#2 ex1   cat2 
#3 ex2   cat1 
#4 ex2   cat2 

当列名存储在vector 中并且想要执行expand 时,这将特别有用

nm1 <- c('x', 'y')
tidyr::expand(dummy, !!! syms(nm1))

在其他一些组合中,!!!symbol 的转换在 character 向量中缺失

【讨论】:

  • 感谢阿克伦!这为我解决了它。不过,我需要更多说明。在哪些情况下我必须首先将字符串转换为符号(使用 sym 术语)?例如,我不需要对mutate 执行此操作,但我记得以前使用filteras.name 执行过类似操作。
  • @Elijah 如果你在函数中传递列名,你可以只在mutate/summarise 中传递不带引号的列名在mutate_at 内,传递字符串或在vars 内不加引号
  • @Elijah 一些功能已被弃用。 filterfilter_atfilter_all 等也有不同的风格。如果您有特定的问题,它会更容易回答,因为您可以通过多种方式进行评估
  • 谢谢阿克伦。不过,我需要更多地思考答案。我脑子里有三个不同版本的dplyr 语法,从旧的lazyeval 方式开始,所以我还不清楚我能做什么,我不能用我对最新语法的了解.
  • @Elijah 忘记lazyeval 旧版本,现在专注于quo/enquo/sym/ensym 等。在将变量传递给函数时使用前缀为en 的那些
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-06
  • 1970-01-01
  • 2016-09-18
  • 1970-01-01
相关资源
最近更新 更多