如何在 R 中使用 dplyr/magrittr 的管道内部函数?
How can I use dplyr/magrittr's pipe inside functions in R?
我正在尝试编写一个函数,它将数据帧和函数名称作为参数。当我尝试使用标准 R 语法编写函数时,我可以使用 eval
和 substitute
获得良好的结果,正如@hadley 在 http://adv-r.had.co.nz/Computing-on-the-language.html
中所推荐的
> 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
现在,当我尝试使用 %>%
运算符编写相同的函数时,它不起作用:
> 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
>
如何将管道运算符与 eval
和 substitute
结合使用?这对我来说似乎真的很棘手。
解决方法是
f <- function(data, x) {
v <- substitute(x)
data %>%
eval(expr = v, envir = .) %>%
mean()
}
问题是管道函数 (%>%
) 正在创建另一个级别的关闭,这会干扰 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)
代码会更容易阅读。
我一直在努力了解我的问题。
首先,我用 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
我不会使用 eval 和 substitute。
以下是适合您问题的 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)
这里发生了两件事:
- 将列名称转换为
enquo()
- 为列添加前缀
!!
更复杂的例子请参考link。
我正在尝试编写一个函数,它将数据帧和函数名称作为参数。当我尝试使用标准 R 语法编写函数时,我可以使用 eval
和 substitute
获得良好的结果,正如@hadley 在 http://adv-r.had.co.nz/Computing-on-the-language.html
> 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
现在,当我尝试使用 %>%
运算符编写相同的函数时,它不起作用:
> 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
>
如何将管道运算符与 eval
和 substitute
结合使用?这对我来说似乎真的很棘手。
解决方法是
f <- function(data, x) {
v <- substitute(x)
data %>%
eval(expr = v, envir = .) %>%
mean()
}
问题是管道函数 (%>%
) 正在创建另一个级别的关闭,这会干扰 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)
代码会更容易阅读。
我一直在努力了解我的问题。
首先,我用 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
我不会使用 eval 和 substitute。
以下是适合您问题的 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)
这里发生了两件事:
- 将列名称转换为
enquo()
- 为列添加前缀
!!
更复杂的例子请参考link。