作为 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

虽然语法描述性不强,但 pipeRvery 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)