在 R 4.1 中引入了一个本地管道运算符,它比以前的实现“更加精简”。我已经注意到 native|>
和 magrittr pipe之间的一个区别%>%
,即2 %>% sqrt
可以工作但2 |> sqrt
不能,并且必须写为2 |> sqrt()
. 使用新的管道运算符时是否有更多差异和陷阱需要注意?
GKi*_*GKi 106
话题 | 马格里特2.0.3 | 基础4.3.0 |
---|---|---|
操作员 | %>% %<>% %$% %!>% %T>% | |> (自 4.1.0 起) |
函数调用 | 1:3 %>% sum() | 1:3 |> sum() |
\xc2\xa0 | 1:3 %>% sum | 需要括号/圆括号 |
\xc2\xa0 | 1:3 %>% `+`(4) | 部分功能不支持 |
插入第一个空位置 | mtcars %>% lm(formula = mpg ~ disp) | mtcars |> lm(formula = mpg ~ disp) |
占位符 | . | _ (自 4.2.0 起) |
\xc2\xa0 | mtcars %>% lm(mpg ~ disp, data = . ) | mtcars |> lm(mpg ~ disp, data = _ ) |
\xc2\xa0 | mtcars %>% lm(mpg ~ disp, . ) | 需要命名参数 |
\xc2\xa0 | 1:3 %>% setNames(., .) | 只能出现一次 |
\xc2\xa0 | 1:3 %>% {sum(sqrt(.))} | 不允许嵌套调用 |
提取呼叫 | mtcars %>% .$cyl mtcars %>% {.$cyl[[3]]} 或者mtcars %$% cyl[[3]] | mtcars |> _$cyl (自 4.3.0 起)mtcars |> _$cyl[[3]] |
环境 | %>% 有附加功能环境 使用: "x" %!>% assign(1) | "x" |> assign(1) |
创建函数 | top6 <- . %>% sort() %>% tail() | 不可能 |
速度 | 由于函数调用的开销而较慢 | 更快,因为语法转换 |
|>
当与(匿名)函数结合使用时,许多差异和限制就会消失:
\n 1 |> (\\(.) .)()
\n-3:3 |> (\\(.) sum(2*abs(.) - 3*.^2))()
另请参阅:如何在基本 R 中纯粹进行管道传输(\'基本管道\')?以及5 个 Magrittr 管道 %>%、%<>%、%$%、%!>% 和 %T>% 的区别和用例是什么?。
\n需要括号
\nlibrary(magrittr)\n\n1:3 |> sum\n#Error: The pipe operator requires a function call as RHS\n\n1:3 |> sum()\n#[1] 6\n\n1:3 |> approxfun(1:3, 4:6)()\n#[1] 4 5 6\n\n1:3 %>% sum\n#[1] 6\n\n1:3 %>% sum()\n#[1] 6\n\n1:3 %>% approxfun(1:3, 4:6) #But in this case empty parentheses are needed\n#Error in if (is.na(method)) stop("invalid interpolation method") :\n1:3 %>% approxfun(1:3, 4:6)()\n#[1] 4 5 6\n
Run Code Online (Sandbox Code Playgroud)\n有些函数不受支持,\n但有些函数仍然可以通过将它们放在括号中、通过函数调用它们::
、使用占位符、在函数中调用或定义函数的链接来调用。
1:3 |> `+`(4)\n#Error: function \'+\' not supported in RHS call of a pipe\n\n1:3 |> (`+`)(4)\n#[1] 5 6 7\n\n1:3 |> base::`+`(4)\n#[1] 5 6 7\n\n1:3 |> `+`(4, e2 = _)\n#[1] 5 6 7\n\n1 |> (`+`)(2) |> (`*`)(3) #(1 + 2) * 3 or `*`(`+`(1, 2), 3) and NOT 1 + 2 * 3\n#[1] 9\n\n1:3 |> (\\(.) . + 4)()\n#[1] 5 6 7\n\nfun <- `+`\n1:3 |> fun(4)\n#[1] 5 6 7\n\n1:3 %>% `+`(4)\n#[1] 5 6 7\n
Run Code Online (Sandbox Code Playgroud)\n占位符需要命名参数
\n2 |> setdiff(1:3, _)\n#Error: pipe placeholder can only be used as a named argument\n\n2 |> setdiff(1:3, y = _)\n#[1] 1 3\n\n2 |> (\\(.) setdiff(1:3, .))()\n#[1] 1 3\n\n2 %>% setdiff(1:3, .)\n#[1] 1 3\n\n2 %>% setdiff(1:3, y = .)\n#[1] 1 3\n
Run Code Online (Sandbox Code Playgroud)\n此外,对于具有...
(点-点-点)参数的可变参数函数,占位符_
需要用作命名参数。
"b" |> paste("a", _, "c")\n#Error: pipe placeholder can only be used as a named argument\n\n"b" |> paste("a", . = _, "c")\n#[1] "a b c"\n\n"b" |> (\\(.) paste("a", ., "c"))()\n#[1] "a b c"\n
Run Code Online (Sandbox Code Playgroud)\n占位符只能出现一次
\n1:3 |> setNames(nm = _)\n#1 2 3 \n#1 2 3 \n\n1:3 |> setNames(object = _, nm = _)\n#Error in setNames(object = "_", nm = "_") : \n# pipe placeholder may only appear once\n\n1:3 |> (\\(.) setNames(., .))()\n#1 2 3 \n#1 2 3 \n\n1:3 |> list() |> setNames(".") |> with(setNames(., .))\n#1 2 3 \n#1 2 3 \n\n1:3 |> list(. = _) |> with(setNames(., .))\n#1 2 3\n#1 2 3\n\n1:3 %>% setNames(object = ., nm = .)\n#1 2 3\n#1 2 3\n\n1:3 %>% setNames(., .)\n#1 2 3 \n#1 2 3\n
Run Code Online (Sandbox Code Playgroud)\n不允许嵌套调用
\n1:3 |> sum(sqrt(x=_))\n#Error in sum(1:3, sqrt(x = "_")) : invalid use of pipe placeholder\n\n1:3 |> (\\(.) sum(sqrt(.)))()\n#[1] 4.146264\n\n1:3 %>% {sum(sqrt(.))}\n#[1] 4.146264\n
Run Code Online (Sandbox Code Playgroud)\n提取调用
\n自 4.3.0 起的实验功能。占位符_
现在还可以在正向管道表达式的 rhs 中用作|>
提取调用中的第一个参数,例如_$coef
。更一般地,它可以用作提取链的头部,例如_$coef[[2]]
*
mtcars |> _$cyl\nmtcars |> _[["cyl"]]\nmtcars |> _[,"cyl"]\n# [1] 6 6 4 6 8 6 8 4 4 6 6 8 8 8 8 8 8 4 4 4 4 8 8 8 8 4 4 4 8 6 8 4\n\nmtcars |> _$cyl[[4]]\n#[1] 6\n\nmtcars %>% .$cyl\nmtcars %>% .[["cyl"]]\nmtcars %>% .[,"cyl"]\n# [1] 6 6 4 6 8 6 8 4 4 6 6 8 8 8 8 8 8 4 4 4 4 8 8 8 8 4 4 4 8 6 8 4\n\n#mtcars %>% .$cyl[4] #gives mtcars[[4]]\nmtcars %>% .$cyl %>% .[4]\n#[1] 6\n
Run Code Online (Sandbox Code Playgroud)\n无需额外环境
\nassign("x", 1)\nx\n#[1] 1\n\n"x" |> assign(2)\nx\n#[1] 2\n\n"x" |> (\\(x) assign(x, 3))()\nx\n#[1] 2\n\n1:3 |> assign("x", value=_)\nx\n#[1] 1 2 3\n\n"x" %>% assign(4)\nx\n#[1] 1 2 3\n\n4 %>% assign("x", .)\nx\n#[1] 1 2 3\n\n"x" %!>% assign(4) #Use instead the eager pipe\nx\n#[1] 4\n\n5 %!>% assign("x", .)\nx\n#[1] 5\n
Run Code Online (Sandbox Code Playgroud)\n创建一个函数
\ntop6 <- . %>% sort() %>% tail()\ntop6(c(1:10,10:1))\n#[1] 8 8 9 9 10 10\n
Run Code Online (Sandbox Code Playgroud)\n其他可能性: \n可以使用Bizarro 管道
实现不同的管道运算符和不同的占位符,这不是管道(请参阅缺点),而是覆盖 ->.;
.
1:3 ->.; sum(.)\n#[1] 6\n\nmtcars ->.; .$cyl\n# [1] 6 6 4 6 8 6 8 4 4 6 6 8 8 8 8 8 8 4 4 4 4 8 8 8 8 4 4 4 8 6 8 4\n\nmtcars ->.; .$cyl[4]\n#[1] 6\n\n1:3 ->.; setNames(., .)\n#1 2 3 \n#1 2 3 \n\n1:3 ->.; sum(sqrt(x=.))\n#[1] 4.146264\n\n"x" ->.; assign(., 5)\nx\n#[1] 5\n\n6 ->.; assign("x", .)\nx\n#[1] 6\n\n1:3 ->.; . + 4\n#[1] 5 6 7\n\n1 ->.; (`+`)(., 2) ->.; (`*`)(., 3)\n#[1] 9\n\n1 ->.; .+2 ->.; .*3\n#[1] 9\n
Run Code Online (Sandbox Code Playgroud)\n并且评价不同。
\nx <- data.frame(a=0)\nf1 <- \\(x) {message("IN 1"); x$b <- 1; message("OUT 1"); x}\nf2 <- \\(x) {message("IN 2"); x$c <- 2; message("OUT 2"); x}\n\nx ->.; f1(.) ->.; f2(.)\n#IN 1\n#OUT 1\n#IN 2\n#OUT 2\n# a b c\n#1 0 1 2\n\nx |> f1() |> f2()\n#IN 2\n#IN 1\n#OUT 1\n#OUT 2\n# a b c\n#1 0 1 2\n\nf2(f1(x))\n#IN 2\n#IN 1\n#OUT 1\n#OUT 2\n# a b c\n#1 0 1 2\n
Run Code Online (Sandbox Code Playgroud)\n或者定义一个自定义管道运算符,该运算.
符在新环境中设置为 lhs 的值并在其中评估 rhs。但这里无法创建或更改调用环境中的值。
`:=` <- \\(lhs, rhs) eval(substitute(rhs), list(. = lhs))\n\nmtcars := .$cyl[4]\n#[1] 6\n\n1:3 := setNames(., .)\n#1 2 3 \n#1 2 3 \n\n1:3 := sum(sqrt(x=.))\n#[1] 4.146264\n\n"x" := assign(., 6)\nx\n#Error: object \'x\' not found\n\n1 := .+2 := .*3\n#[1] 9\n
Run Code Online (Sandbox Code Playgroud)\n因此,另一种尝试是将 lhs 分配给调用环境中的占位符.
,并在调用环境中评估 rhs。但.
如果它已经存在,这里将从调用环境中删除。
`?` <- \\(lhs, rhs) {\n on.exit(if(exists(".", parent.frame())) rm(., envir = parent.frame()))\n assign(".", lhs, envir=parent.frame())\n eval.parent(substitute(rhs))\n}\n\nmtcars ? .$cyl[4]\n#[1] 6\n\n1:3 ? setNames(., .)\n#1 2 3 \n#1 2 3 \n\n1:3 ? sum(sqrt(x=.))\n#[1] 4.146264\n\n"x" ? assign(., 6)\nx\n#[1] 6\n\n1 ? .+2 ? .*3\n#[1] 9\n
Run Code Online (Sandbox Code Playgroud)\n另一种可能性是将 all 替换.
为 lhs,以便在评估过程中.
不再作为名称存在。
`%|>%` <- \\(lhs, rhs)\n eval.parent(eval(call(\'substitute\', substitute(rhs), list(. = lhs))))\n\nmtcars %|>% .$cyl[4]\n[1] 6\n\n1:3 %|>% setNames(., .)\n1 2 3 \n1 2 3\n\n1:3 %|>% sum(sqrt(x=.))\n[1] 4.146264\n\n"x" %|>% assign(., 6)\nx\n#[1] 6\n\n1 %|>% .+2 %|>% .*3\n#[1] 7\n
Run Code Online (Sandbox Code Playgroud)\n使用的运算符的名称会影响运算符优先级:请参阅相同的函数,但使用名称 %>% 与使用名称 := 相比会导致不同的结果。
\n有关更高级的选项,请参阅:编写自己的/自定义管道运算符。
速度
\nlibrary(magrittr)\n\n`:=` <- \\(lhs, rhs) eval(substitute(rhs), list(. = lhs))\n\n`?` <- \\(lhs, rhs) {\n on.exit(if(exists(".", parent.frame())) rm(., envir = parent.frame()))\n assign(".", lhs, envir=parent.frame())\n eval.parent(substitute(rhs))\n}\n\n`%|>%` <- \\(lhs, rhs)\n eval.parent(eval(call(\'substitute\', substitute(rhs), list(. = lhs))))\n\n\nx <- 42\nbench::mark(min_time = 0.2, max_iterations = 1e8\n, x\n, identity(x)\n, "|>" = x |> identity()\n, "|> _" = x |> identity(x=_)\n, "->.;" = {x ->.; identity(.)}\n, "|> f()" = x |> (\\(y) identity(y))()\n, "%>%" = x %>% identity\n, ":=" = x := identity(.)\n, "list." = x |> list() |> setNames(".") |> with(identity(.))\n, "%|>%" = x %|>% identity(.)\n, "?" = x ? identity(.)\n)\n
Run Code Online (Sandbox Code Playgroud)\n结果
\n expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc\n <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl> <int> <dbl>\n 1 x 31.08ns 48.2ns 19741120. 0B 7.46 2646587 1\n 2 identity(x) 491.04ns 553.09ns 1750116. 0B 27.0 323575 5\n 3 |> 497.91ns 548.08ns 1758553. 0B 27.3 322408 5\n 4 |> _ 506.87ns 568.92ns 1720374. 0B 26.9 320003 5\n 5 ->.; 725.03ns 786.04ns 1238488. 0B 21.2 233864 4\n 6 |> f() 972.07ns 1.03\xc2\xb5s 929926. 0B 37.8 172288 7\n 7 %>% 2.76\xc2\xb5s 3.05\xc2\xb5s 315448. 0B 37.2 59361 7\n 8 := 3.02\xc2\xb5s 3.35\xc2\xb5s 288025. 0B 37.0 54561 7\n 9 list. 5.19\xc2\xb5s 5.89\xc2\xb5s 166721. 0B 36.8 31752 7\n10 %|>% 6.01\xc2\xb5s 6.86\xc2\xb5s 143294. 0B 37.0 27076 7\n11 ? 30.9\xc2\xb5s 32.79\xc2\xb5s 30074. 0B 31.3 5768 6\n
Run Code Online (Sandbox Code Playgroud)\n
Maë*_*aël 18
一个区别是它们的占位符,_
在基数 R 中,.
在magrittr
.
从R 4.2.0开始,基础 R 管道有一个用于管道输入值的占位符 ,_
类似于%>%
's .
,但其使用仅限于命名参数,并且每次调用只能使用一次。
现在可以在 rhs 调用中使用带有占位符 _ 的命名参数来指定 lhs 的插入位置。占位符只能在右侧出现一次。
重申Ronak Shah的示例,您现在可以用作_
右侧的命名参数来引用公式的左侧:
c("dogs", "cats", "rats") |>
grepl("at", x = _)
#[1] FALSE TRUE TRUE
Run Code Online (Sandbox Code Playgroud)
但它必须被命名为:
c("dogs", "cats", "rats") |>
grepl("at", _)
#Error: pipe placeholder can only be used as a named argument
Run Code Online (Sandbox Code Playgroud)
并且不能出现多次(为了解决这个问题,仍然可以使用Ronak Shah提供的解决方案):
c("dogs", "cats", "rats") |>
expand.grid(x = _, y = _)
# Error in expand.grid(x = "_", y = "_") : pipe placeholder may only appear once
Run Code Online (Sandbox Code Playgroud)
虽然这是可能的magrittr
:
library(magrittr)
c("dogs", "cats", "rats") %>%
expand.grid(x = ., y = .)
# x y
#1 dogs dogs
#2 cats dogs
#3 rats dogs
#4 dogs cats
#5 cats cats
#6 rats cats
#7 dogs rats
#8 cats rats
#9 rats rats
Run Code Online (Sandbox Code Playgroud)
Ron*_*hah 12
它们两者之间的另一个区别是在值管道.
可被用作在一个占位符magrittr
的管
c("dogs", "cats", "rats") %>% grepl("at", .)
#[1] FALSE TRUE TRUE
Run Code Online (Sandbox Code Playgroud)
但这对于 R 的原生管道是不可能的。
c("dogs", "cats", "rats") |> grepl("at", .)
Run Code Online (Sandbox Code Playgroud)
grepl(c("dogs", "cats", "rats"), "at", .) 中的错误:对象 '.' 未找到
以下是引用它们的不同方法 -
find_at = function(x) grepl("at", x)
c("dogs", "cats", "rats") |> find_at()
#[1] FALSE TRUE TRUE
Run Code Online (Sandbox Code Playgroud)
2 一个。使用匿名函数 -
c("dogs", "cats", "rats") |> {function(x) grepl("at", x)}()
Run Code Online (Sandbox Code Playgroud)
2 湾。使用新的匿名函数语法
c("dogs", "cats", "rats") |> {\(x) grepl("at", x)}()
Run Code Online (Sandbox Code Playgroud)
示例取自 - https://www.jumpingrivers.com/blog/new-features-r410-pipe-anonymous-functions/
Dir*_*tel 12
|>
R 4.1.0 中添加的基础 R 管道“只是”执行功能组合。即我们可以看到它的使用真的和函数调用一样:
> 1:5 |> sum() # simple use of |>
[1] 15
> deparse(substitute( 1:5 |> sum() ))
[1] "sum(1:5)"
>
Run Code Online (Sandbox Code Playgroud)
这有一些后果:
sum()
这里需要括号才能正确调用这导致可能使用=>
当前“可用但未激活”(为此您需要设置环境变量_R_USE_PIPEBIND_
,并且可能会在 R 4.2.0 中更改)。
(这首先是作为一个问题的答案在这里复制的,我只是按照建议复制了它。)
编辑:随着关于“是什么=>
”的后续问题出现,这里是一个快速跟进。请注意,此运算符可能会发生变化。
> Sys.setenv("_R_USE_PIPEBIND_"=TRUE)
> mtcars |> subset(cyl == 4) |> d => lm(mpg ~ disp, data = d)
Call:
lm(formula = mpg ~ disp, data = subset(mtcars, cyl == 4))
Coefficients:
(Intercept) disp
40.872 -0.135
> deparse(substitute(mtcars |> subset(cyl==4) |> d => lm(mpg ~ disp, data = d)))
[1] "lm(mpg ~ disp, data = subset(mtcars, cyl == 4))"
>
Run Code Online (Sandbox Code Playgroud)
在deparse(substitute(...))
这里特别漂亮。
sie*_*ste 11
本机管道是作为语法转换实现的,因此2 |> sqrt()
与 相比没有明显的开销sqrt(2)
,而2 %>% sqrt()
有一个小的惩罚。
microbenchmark(sqrt(1),
2 |> sqrt(),
3 %>% sqrt())
# Unit: nanoseconds
# expr min lq mean median uq max neval
# sqrt(1) 117 126.5 141.66 132.0 139 246 100
# sqrt(2) 118 129.0 156.16 134.0 145 1792 100
# 3 %>% sqrt() 2695 2762.5 2945.26 2811.5 2855 13736 100
Run Code Online (Sandbox Code Playgroud)
您会看到2 |> sqrt()
传递给的表达式如何microbenchmark
被解析为sqrt(2)
. 这也可以在
quote(2 |> sqrt())
# sqrt(2)
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
671 次 |
最近记录: |