如何将管道链(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 我从没想过->
会有理由。现在有了!
但是如果你只使用->
,你就不能继续使用链:例如data.frame( x = c(1:3), y = (4:6)) %>% sum -> a %>% exp
给出一个错误,你将不得不使用括号(data.frame( x = c(1:3), y = (4:6)) %>% sum -> a) %>% (exp)
,如果一个不小心这可能会导致意想不到的结果。
G. Grothendieck,我已经尝试过上面的代码(data.frame( x = c(1:3), y = (4:6)) %>% (sum -> a) %>% exp
),但它没有正确地将部分结果分配给a
。
(1) 继续链条不是一个好主意。如果你真的想通过它更好地分配一个变量,使其成为两个链。仍然可以这样写:data.frame( x = c(1:3), y = (4:6)) %>% sum -> a; a %>% exp
以避免该问题。 (2) 部分赋值是副作用,函数式风格是为了避免副作用。
怎么样:tee=function(v,n)assign(n,v,.GlobalEnv);v
然后d %>% whatever %>% tee("part1") %>% otherstuff $>$ tee("part2") %>% 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】:您也可以使用<<-
运算符:
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)
这将正确创建a
、b
和c
。
【讨论】:
您确定它不适用于<-
吗?如果没有严格要求,最好不要使用<<-
。
@CarlWitthoft,不会的,<-
运算符将分配给函数的本地环境,因此它不会在全局环境中创建变量。【参考方案4】:
使用 pipeR 的 %>>%
这应该很容易。
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】:我喜欢做的(我在不记得的地方发现了这个技巧)是在我的管道链末端使用. -> obj
。这样,我只需插入一个新行就可以在链的末尾添加额外的步骤,而不必重新定位到 ->
赋值运算符。
您也可以使用(.)
而不是.
,但它看起来有点,奇怪。
例如,而不是这个:
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
它使您可以更轻松地查看管道数据的最终位置。此外,虽然看起来没什么大不了的,但添加另一个最后一步会更容易,因为您不需要将 ->
移动到新行,只需在 .
之前添加新行并添加步骤。
像这样:
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
)。我似乎很懒惰,但使用%>%
是关于速度。
所以我将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)的结果提供给对象的主要内容,如果未能解决你的问题,请参考以下文章