R中的管道符(%>%)不能与replicate函数一起使用

16

我正在尝试学习管道函数 (%>%)。
当尝试将这行代码转换为另一行代码时,它不起作用。

---- R 代码 -- 原始版本 -----

set.seed(1014)
replicate(6,sample(1:8))
     [,1] [,2] [,3] [,4] [,5] [,6]
[1,]    1    3    7    4    5    1
[2,]    2    8    4    2    4    2
[3,]    5    4    8    5    8    5
[4,]    3    1    2    1    1    7
[5,]    4    6    3    7    7    3
[6,]    6    5    1    3    3    8
[7,]    8    7    5    8    6    6
[8,]    7    2    6    6    2    4

---- 使用管道重构的R代码 ----

> sample(1:8) %>%  replicate(6,.)
     [,1] [,2] [,3] [,4] [,5] [,6]
[1,]    7    7    7    7    7    7
[2,]    3    3    3    3    3    3
[3,]    2    2    2    2    2    2
[4,]    1    1    1    1    1    1
[5,]    5    5    5    5    5    5
[6,]    4    4    4    4    4    4
[7,]    8    8    8    8    8    8
[8,]    6    6    6    6    6    6

注意,当使用管道时,抽样不起作用,导致我得到相同的向量。


5
第一个代码中,replicate函数执行sample函数6次。第二个代码中,你已经得到了sample函数的输出值,并将该输出值复制了6次。 - student
2个回答

21

这是可以预料的。replicate期望表达式,但当直接使用管道操作符时,你只是将对sample()的调用结果粘贴到replicate中。因此,你会得到六次相同的结果。

你需要使用quote()将表达式传递给replicate,而不是结果,但你不应该忘记对该表达式的每个重复进行求值。

quote(sample(c(1:10,-99),6,rep=TRUE)) %>% 
  replicate(6, .) %>%
  sapply(eval)

提供:

    [,1] [,2] [,3] [,4] [,5] [,6]
[1,]    5    2   10   10    9    2
[2,]    4    3    1    3  -99    1
[3,]   10    2    3    8    2    4
[4,]  -99    1    6    2   10    3
[5,]    8  -99    1    9    4    6
[6,]    4   10    8    1  -99    8

这里会发生什么:

  • 管道将表达式发送到复制而不对其进行评估。
  • 复制将该表达式复制并返回一个列表,其中包含6次该表达式,但是没有对其进行评估。
  • sapply(eval)遍历列表并执行其中的每个表达式。

在您之前的问题中(即使用data.frame时),您可以执行以下操作:

quote(sample(c(1:10,-99),6,rep=TRUE)) %>% 
  replicate(6, .) %>%
  data.frame

现在函数data.frame会强制执行表达式,但你最终会得到糟糕的变量名,即表达式本身。

如果你想了解更多有关此处问题的信息,你需要深入学习所谓的“惰性求值”以及管道运算符如何处理它。但说实话,我真的看不出在这种情况下使用管道操作符有什么优点。它甚至不会更易读。

根据Frank的评论:你可以使用管道和函数嵌套的混合方式来避免sapply。但为此,你必须将嵌套的函数包含在代码块中,否则管道操作符将无法正确处理它:

quote(sample(c(1:10,-99),6,rep=TRUE)) %>% {
  replicate(6, eval(.)) }

非常有趣,但在我看来并不是非常有用...


非常感谢。这让我很有感触。只是想学习这些东西。是的,在这里使用管道可能没有什么意义,但我只是在学习。 - phage
我认为不需要使用sapply:quote(sample(c(1:10,-99),6,rep=TRUE)) %>% { replicate(6, eval(.)) } - Frank
1
@Frank,忘记我之前的评论了,我错过了{}eval()现在嵌套在replicate内部的事实。确实可以工作,但这是一半管道,一半不是,所以它甚至更没有意义。谢谢你提到它,这是一个有趣的案例。这也是为什么我不愿打开管道运算符的潘多拉魔盒的另一个例子。不过我已经将您的建议添加到帖子中了。 - Joris Meys
4
在我看来,它仍然使用了管道,只不过没有传递给replicate的第一个参数。magrittr有关于何时猜测将.传递给第一个参数的规则让人感到烦恼。此外,对于模拟,我不会使用管道,在这里运行了一个基准测试(在“速度和管道”下):http://franknarf1.github.io/r-tutorial/_book/work.html#simulations - Frank

0
截至 R 4.1.0,可用了一个新的管道|>,它可以与复制一起使用。
library(magrittr)
set.seed(123)
runif(1) %>%
  replicate(n = 4)
# 0.2875775 0.2875775 0.2875775 0.2875775
runif(1) |>
  replicate(n = 4)
# 0.7883051 0.4089769 0.8830174 0.9404673

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接