【问题标题】:How can I use dplyr/magrittr's pipe inside functions in R?如何在 R 中的函数内部使用 dplyr/magrittr 的管道?
【发布时间】:2016-05-22 14:55:36
【问题描述】:

我正在尝试编写一个函数,该函数将数据框和函数名称作为参数。当我尝试使用标准 R 语法编写函数时,我可以使用 @hadley 在 http://adv-r.had.co.nz/Computing-on-the-language.html 中推荐的 evalsubstitute 获得良好的结果

> df <- data.frame(y = 1:10)
> f <- function(data, x) {
+   out <- mean(eval(expr = substitute(x), envir = data))
+   return(out)
+ }
> f(data = df, x = y)
[1] 5.5

现在,当我尝试使用 %&gt;% 运算符编写相同的函数时,它不起作用:

> df <- data.frame(y = 1:10)
> f <- function(data, x) {
+   data %>% 
+     eval(expr = substitute(x), envir = .) %>% 
+     mean()
+ }
> f(data = df, x = y)
Show Traceback
Rerun with Debug
 Error in eval(expr, envir, enclos) : objet 'y' introuvable 
> 

如何将管道运算符与 evalsubstitute 结合使用?这对我来说似乎真的很棘手。

【问题讨论】:

标签: r dplyr magrittr nse


【解决方案1】:

解决方法是

f <- function(data, x) {
  v <- substitute(x)
  data %>% 
    eval(expr = v, envir = .) %>%
    mean()
}

问题在于管道函数 (%&gt;%) 正在创建另一个级别的闭包,这会干扰对 substitute(x) 的评估。你可以通过这个例子看出区别

df <- data.frame(y = 1:10)
f1 <- function(data, x) {
  print(environment())
  eval(expr = environment(), envir = data)
}

f2 <- function(data, x) {
  print(environment())
  data %>% 
    eval(expr = environment(), envir = .)
}
f1(data = df, x = y)
# <environment: 0x0000000006388638>
# <environment: 0x0000000006388638>
f2(data = df, x = y)
# <environment: 0x000000000638a4a8>
# <environment: 0x0000000005f91ae0>

请注意 matrittr 版本中的环境有何不同。在处理非标准评估时,您想尽快处理substitute 的事情。

我希望你的用例比你的例子复杂一点,因为它看起来像

mean(df$y)

将是更容易阅读的代码。

【讨论】:

  • 当然,这只是一个玩具示例,我想写一个更复杂的函数:)
  • 您的解决方案很优雅。但是,对于将substitute 与管道结合的问题,还有更一般的答案吗?
  • Hadley 说这不是一个好的解决方案:github.com/hadley/r4ds/issues/45#issuecomment-184855725。那么有什么好的解决方案呢?
  • 问哈德利。我建议避免在 magrittr 中使用替代和评估。
【解决方案2】:

我一直在努力理解我的问题。

首先,我用summarise() 函数写了我想要的:

> library(dplyr)
> df <- data.frame(y = 1:10)
> summarise_(.data = df, mean = ~mean(y))
  mean
1  5.5

然后我尝试编写自己的函数。我找到了一个似乎适用于this post 中的lazyeval 包的解决方案。我使用lazy()interp() 函数来写我想要的。

第一种可能性在这里:

> library(lazyeval)
> f <- function(data, col) {
+   col <- lazy(col)
+   inter <- interp(~mean(x), x = col)
+   summarise_(.data = data, mean = inter)    
+   }
> f(data = df, col = y)
  mean
1  5.5

我也可以使用管道:

> f <- function(data, col) {
+   col <- lazy(col)
+   inter <- interp(~mean(x), x = col)
+   data %>% 
+     summarise_(.data = ., mean = inter)    
+ }
> 
> f(data = df, col = y)
  mean
1  5.5

【讨论】:

    【解决方案3】:

    我不会使用 eval 和替换。

    以下是适合您的问题的this great post 的简化版本。

    df <- data.frame(y = 1:10)
    f <- function(data, x) {
      x <- enquo(x)
      df %>% summarise(mean = mean(!!x))
       }
    f(data = df, x = y)
    

    这里发生了两件事:

    1. enquo() 转换列名
    2. 为列添加前缀!!

    有关更复杂的示例,请参阅链接。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-12-27
      • 2023-01-13
      • 2017-10-02
      • 1970-01-01
      • 2018-07-23
      • 2015-03-12
      相关资源
      最近更新 更多