作为 dplyr 管道的一部分,将中间输出分配给临时变量
Assign intermediate output to temp variable as part of dplyr pipeline
问:在 R dplyr 管道中,如何将一些中间输出分配给临时变量以便在管道中进一步使用?
我的以下方法有效。但它分配到全局框架中,这是不可取的。必须有更好的方法,对吗?我认为涉及注释行的方法会得到预期的结果。没有骰子。困惑为什么那不起作用。
df <- data.frame(a = LETTERS[1:3], b=1:3)
df %>%
filter(b < 3) %>%
assign("tmp", ., envir = .GlobalEnv) %>% # works
#assign("tmp", .) %>% # doesn't work
mutate(b = b*2) %>%
bind_rows(tmp)
a b
1 A 2
2 B 4
3 A 1
4 B 2
您可以在管道中需要的位置生成所需的对象。例如:
df %>% filter(b < 3) %>% mutate(b = b*2) %>%
bind_rows(df %>% filter(b < 3))
此方法避免了两次过滤:
df %>%
filter(b < 3) %>%
bind_rows(., mutate(., b = b*2))
pipeR
是一个扩展管道功能的包,无需添加不同的管道(如 magrittr
所做的那样)。要分配,您传递一个变量名,在括号中用 ~
引用作为管道中的元素:
library(dplyr)
library(pipeR)
df %>>%
filter(b < 3) %>>%
(~tmp) %>>%
mutate(b = b*2) %>>%
bind_rows(tmp)
## a b
## 1 A 2
## 2 B 4
## 3 A 1
## 4 B 2
tmp
## a b
## 1 A 1
## 2 B 2
虽然语法描述性不强,但 pipeR
是 very well documented。
这不会在全局环境中创建对象:
df %>%
filter(b < 3) %>%
{
{ . -> tmp } %>%
mutate(b = b*2) %>%
bind_rows(tmp)
}
如果您使用 . ->> tmp
而不是 . -> tmp
或将其插入管道,这也可用于调试:
{ browser(); . } %>%
我对这个问题感兴趣是为了调试(想保存中间结果,以便我可以从控制台检查和操作它们,而不必将管道分成两部分,这很麻烦。所以,对于我目的,OP 解决方案原始解决方案的唯一问题是它有点冗长。
这可以通过定义辅助函数来解决:
to_var <- function(., ..., env=.GlobalEnv) {
var_name = quo_name(quos(...)[[1]])
assign(var_name, ., envir=env)
.
}
然后可以按如下方式使用:
df <- data.frame(a = LETTERS[1:3], b=1:3)
df %>%
filter(b < 3) %>%
to_var(tmp) %>%
mutate(b = b*2) %>%
bind_rows(tmp)
# tmp still exists here
这仍然使用全局环境,但您也可以显式传递更局部的环境,如下例所示:
f <- function() {
df <- data.frame(a = LETTERS[1:3], b=1:3)
env = environment()
df %>%
filter(b < 3) %>%
to_var(tmp, env=env) %>%
mutate(b = b*2) %>%
bind_rows(tmp)
}
f()
# tmp does not exist here
的问题在于它似乎无法与 tidyverse 管道开箱即用。
G. Grothendieck 的解决方案 根本不适用于调试用例。(更新:请参阅下面 G. Grothendieck 的评论和他更新的答案!)
最后,assign("tmp", .) %>%
不起作用的原因是 assign()
的默认 'envir' 参数是 "current environment"(参见 documentation for assign)在流水线的每个阶段都是不同的。要看到这一点,请尝试在不同的点将 { print(environment()); . } %>%
插入到管道中,并查看每次打印不同的地址。 (可能可以调整 to_var 的定义,以便默认为祖父母环境。)
我经常发现需要在管道中保存中间产品。虽然我的用例通常是为了避免为以后的拆分、操作和重组而复制过滤器,但该技术在这里可以很好地工作:
df %>%
filter(b < 3) %>%
{. ->> intermediateResult} %>% # this saves intermediate
mutate(b = b*2) %>%
bind_rows(intermediateResult)
问:在 R dplyr 管道中,如何将一些中间输出分配给临时变量以便在管道中进一步使用?
我的以下方法有效。但它分配到全局框架中,这是不可取的。必须有更好的方法,对吗?我认为涉及注释行的方法会得到预期的结果。没有骰子。困惑为什么那不起作用。
df <- data.frame(a = LETTERS[1:3], b=1:3)
df %>%
filter(b < 3) %>%
assign("tmp", ., envir = .GlobalEnv) %>% # works
#assign("tmp", .) %>% # doesn't work
mutate(b = b*2) %>%
bind_rows(tmp)
a b
1 A 2
2 B 4
3 A 1
4 B 2
您可以在管道中需要的位置生成所需的对象。例如:
df %>% filter(b < 3) %>% mutate(b = b*2) %>%
bind_rows(df %>% filter(b < 3))
此方法避免了两次过滤:
df %>%
filter(b < 3) %>%
bind_rows(., mutate(., b = b*2))
pipeR
是一个扩展管道功能的包,无需添加不同的管道(如 magrittr
所做的那样)。要分配,您传递一个变量名,在括号中用 ~
引用作为管道中的元素:
library(dplyr)
library(pipeR)
df %>>%
filter(b < 3) %>>%
(~tmp) %>>%
mutate(b = b*2) %>>%
bind_rows(tmp)
## a b
## 1 A 2
## 2 B 4
## 3 A 1
## 4 B 2
tmp
## a b
## 1 A 1
## 2 B 2
虽然语法描述性不强,但 pipeR
是 very well documented。
这不会在全局环境中创建对象:
df %>%
filter(b < 3) %>%
{
{ . -> tmp } %>%
mutate(b = b*2) %>%
bind_rows(tmp)
}
如果您使用 . ->> tmp
而不是 . -> tmp
或将其插入管道,这也可用于调试:
{ browser(); . } %>%
我对这个问题感兴趣是为了调试(想保存中间结果,以便我可以从控制台检查和操作它们,而不必将管道分成两部分,这很麻烦。所以,对于我目的,OP 解决方案原始解决方案的唯一问题是它有点冗长。
这可以通过定义辅助函数来解决:
to_var <- function(., ..., env=.GlobalEnv) {
var_name = quo_name(quos(...)[[1]])
assign(var_name, ., envir=env)
.
}
然后可以按如下方式使用:
df <- data.frame(a = LETTERS[1:3], b=1:3)
df %>%
filter(b < 3) %>%
to_var(tmp) %>%
mutate(b = b*2) %>%
bind_rows(tmp)
# tmp still exists here
这仍然使用全局环境,但您也可以显式传递更局部的环境,如下例所示:
f <- function() {
df <- data.frame(a = LETTERS[1:3], b=1:3)
env = environment()
df %>%
filter(b < 3) %>%
to_var(tmp, env=env) %>%
mutate(b = b*2) %>%
bind_rows(tmp)
}
f()
# tmp does not exist here
G. Grothendieck 的解决方案 根本不适用于调试用例。(更新:请参阅下面 G. Grothendieck 的评论和他更新的答案!)
最后,assign("tmp", .) %>%
不起作用的原因是 assign()
的默认 'envir' 参数是 "current environment"(参见 documentation for assign)在流水线的每个阶段都是不同的。要看到这一点,请尝试在不同的点将 { print(environment()); . } %>%
插入到管道中,并查看每次打印不同的地址。 (可能可以调整 to_var 的定义,以便默认为祖父母环境。)
我经常发现需要在管道中保存中间产品。虽然我的用例通常是为了避免为以后的拆分、操作和重组而复制过滤器,但该技术在这里可以很好地工作:
df %>%
filter(b < 3) %>%
{. ->> intermediateResult} %>% # this saves intermediate
mutate(b = b*2) %>%
bind_rows(intermediateResult)