如何将管道链(magrittr)的结果提供给对象

Posted

技术标签:

【中文标题】如何将管道链(magrittr)的结果提供给对象【英文标题】:how to feed the result of a pipe chain (magrittr) to an object 【发布时间】:2014-11-16 12:32:06 【问题描述】:

这是一个相当简单的问题。但是我找不到每个 google/stackexchange 的答案并查看 magrittr 的文档。 您如何提供通过 %>% 连接的函数链的结果以创建向量?

我看到大多数人的做法是:

a <-
data.frame( x = c(1:3), y = (4:6)) %>%
sum()

但也有一个解决方案,我可以将结果通过管道链接到一个对象,可能是别名或类似的东西,有点像这样:

data.frame( x = c(1:3), y = (4:6)) %>%
sum() %>%
a <- ()

这将有助于将所有代码保持在相同的逻辑中,将结果向前“向下传递”。

【问题讨论】:

【参考方案1】:

试试这个:

data.frame( x = c(1:3), y = (4:6)) %>% sum -> a

【讨论】:

+1 我从没想过-&gt; 会有理由。现在有了! 但是如果你只使用-&gt;,你就不能继续使用链:例如data.frame( x = c(1:3), y = (4:6)) %&gt;% sum -&gt; a %&gt;% exp给出一个错误,你将不得不使用括号(data.frame( x = c(1:3), y = (4:6)) %&gt;% sum -&gt; a) %&gt;% (exp),如果一个不小心这可能会导致意想不到的结果。 G. Grothendieck,我已经尝试过上面的代码(data.frame( x = c(1:3), y = (4:6)) %&gt;% (sum -&gt; a) %&gt;% exp),但它没有正确地将部分结果分配给a (1) 继续链条不是一个好主意。如果你真的想通过它更好地分配一个变量,使其成为两个链。仍然可以这样写:data.frame( x = c(1:3), y = (4:6)) %&gt;% sum -&gt; a; a %&gt;% exp 以避免该问题。 (2) 部分赋值是副作用,函数式风格是为了避免副作用。 怎么样:tee=function(v,n)assign(n,v,.GlobalEnv);v 然后d %&gt;% whatever %&gt;% tee("part1") %&gt;% otherstuff $&gt;$ tee("part2") %&gt;% etcetc【参考方案2】:

你可以这样做:

data.frame( x = c(1:3), y = (4:6)) %>%  
sum %>%  
assign(x="a",value=.,pos=1)  

有几点需要注意:

您可以使用“。”告诉magrittr 被提出的对象属于哪个参数。默认情况下它是第一个,但这里我使用. 表示我希望它在第二个value 参数中。

其次,我必须使用 pos=1 参数在全局环境中进行分配。

【讨论】:

您真的不应该将 assign 用于任何事情。这是不好的编码习惯。 @iShouldUseAName 你能扩展一下吗?这里有什么危险?【参考方案3】:

您也可以使用&lt;&lt;- 运算符:

data.frame( x = c(1:3), y = (4:6)) %>%
  sum() %>%
  `<<-`(a,.)

编辑:我认为 John Paul 的建议是最安全的建议,您可以继续使用链对部分结果进行不同的分配。例如:

data.frame( x = c(1:3), y = (4:6)) %>%  
  sum %>%  
  assign(x="a",value=., pos=1)  %>% 
  exp %>%
  assign(x="b",value=., pos=1) %>% 
  sqrt %>%
  assign(x="c", value=., pos=1)

这将正确创建abc

【讨论】:

您确定它不适用于 &lt;- 吗?如果没有严格要求,最好不要使用&lt;&lt;- @CarlWitthoft,不会的,&lt;- 运算符将分配给函数的本地环境,因此它不会在全局环境中创建变量。【参考方案4】:

使用 pipeR 的 %&gt;&gt;% 这应该很容易。

library(pipeR)
data.frame( x = c(1:3), y = (4:6)) %>>%
  sum %>>%
  (~ a)

pipeR 教程可能会有所帮助:http://renkun.me/pipeR-tutorial/ 分配:http://renkun.me/pipeR-tutorial/Pipe-operator/Pipe-with-assignment.html

【讨论】:

【参考方案5】:

我喜欢做的(我在不记得的地方发现了这个技巧)是在我的管道链末端使用. -&gt; obj。这样,我只需插入一个新行就可以在链的末尾添加额外的步骤,而不必重新定位到 -&gt; 赋值运算符。

您也可以使用(.) 而不是.,但它看起来有点,奇怪

例如,而不是这个:

  iris %>% 
    ddply(.(Species), summarise, 
          mean.petal = mean(Petal.Length),
          mean.sepal = mean(Sepal.Length)) -> summary

这样做:

iris %>% 
    ddply(.(Species), summarise, 
          mean.petal = mean(Petal.Length),
          mean.sepal = mean(Sepal.Length)) %>% 
    . -> summary

它使您可以更轻松地查看管道数据的最终位置。此外,虽然看起来没什么大不了的,但添加另一个最后一步会更容易,因为您不需要将 -&gt; 移动到新行,只需在 . 之前添加新行并添加步骤。

像这样:

iris %>% 
    ddply(.(Species), summarise, 
          mean.petal = mean(Petal.Length),
          mean.sepal = mean(Sepal.Length)) %>% 
    arrange(desc(mean.petal)) %>%   # just add a step here
    . -> summary

但这无助于保存中间结果。 John Paul 对使用 assign() 的回答很好,但打字有点长。您需要使用.,因为数据不是第一个参数,您必须将新参数的名称放在"" 中,并指定环境(pos = 1)。我似乎很懒惰,但使用%&gt;% 是关于速度

所以我将assign() 包裹在一个小函数中,这会加快一点速度:

keep <- function(x, name) assign(as.character(substitute(name)), x, pos = 1)

所以现在你可以这样做了:

  keep <- function(x, name) assign(as.character(substitute(name)), x, pos = 1)

  iris %>% 
    ddply(.(Species), summarise, 
          mean.petal = mean(Petal.Length),
          mean.sepal = mean(Sepal.Length)) %>% keep(unsorted.data) %>% # keep this step
    arrange(mean.petal) %>%
    . -> sorted.data

sorted.data
#     Species mean.petal mean.sepal
#1     setosa      1.462      5.006
#2 versicolor      4.260      5.936
#3  virginica      5.552      6.588

unsorted.data
#     Species mean.petal mean.sepal
#1     setosa      1.462      5.006
#2 versicolor      4.260      5.936
#3  virginica      5.552      6.588

【讨论】:

以上是关于如何将管道链(magrittr)的结果提供给对象的主要内容,如果未能解决你的问题,请参考以下文章

获取在“magrittr”管道调用的函数中计算为点的表达式

R:在自写包中使用 magrittr 管道运算符

R:在自编写的包中使用magrittr管道运算符

%.% (dplyr) 和 %>% (magrittr) 之间的差异

在不输入第一个参数的情况下使用管道

将变量乘以管道