|>
和 magrittr 管道 %>%
之间的一个区别,即 2 %>% sqrt
是有效的,但 2 |> sqrt
不行,必须写成 2 |> sqrt()
。在使用原生管道操作符时,还有其他的区别和注意事项吗?主题 | Magrittr 2.0.3 | Base 4.3.0 |
---|---|---|
运算符 | %>% %<>% %$% %!>% %T>% |
|> (自4.1.0起) |
函数调用 | 1:3 %>% sum() |
1:3 |> sum() |
1:3 %>% sum |
需要括号 | |
1:3 %>% `+`(4) |
不支持某些函数 | |
插入到第一个空位置 | mtcars %>% lm(formula = mpg ~ disp) |
mtcars |> lm(formula = mpg ~ disp) |
占位符 | . |
_ (自4.2.0起) |
mtcars %>% lm(mpg ~ disp, data = . ) |
mtcars |> lm(mpg ~ disp, data = _ ) |
|
mtcars %>% lm(mpg ~ disp, . ) |
需要命名参数 | |
1:3 %>% setNames(., .) |
只能出现一次 | |
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() |
不可能 |
速度 | 较慢,因为函数调用的开销 | 较快,因为语法转换 |
|>
与(匿名)函数结合时消失了:1 |> (\(.) .)()
-3:3 |> (\(.) sum(2*abs(.) - 3*.^2))()
还可以参考:如何在基本R中进行纯粹的管道操作('base pipe')?和五个Magrittr管道符号%>%,%<>%,%$%,%!>%和%T>%的区别和用法是什么?。
需要括号
library(magrittr)
1:3 |> sum
#Error: The pipe operator requires a function call as RHS
1:3 |> sum()
#[1] 6
1:3 |> approxfun(1:3, 4:6)()
#[1] 4 5 6
1:3 %>% sum
#[1] 6
1:3 %>% sum()
#[1] 6
1:3 %>% approxfun(1:3, 4:6) #But in this case empty parentheses are needed
#Error in if (is.na(method)) stop("invalid interpolation method") :
1:3 %>% approxfun(1:3, 4:6)()
#[1] 4 5 6
::
调用它们,使用占位符,在函数中调用它,或者定义一个指向该函数的链接。1:3 |> `+`(4)
#Error: function '+' not supported in RHS call of a pipe
1:3 |> (`+`)(4)
#[1] 5 6 7
1:3 |> base::`+`(4)
#[1] 5 6 7
1:3 |> `+`(4, e2 = _)
#[1] 5 6 7
1 |> (`+`)(2) |> (`*`)(3) #(1 + 2) * 3 or `*`(`+`(1, 2), 3) and NOT 1 + 2 * 3
#[1] 9
1:3 |> (\(.) . + 4)()
#[1] 5 6 7
fun <- `+`
1:3 |> fun(4)
#[1] 5 6 7
1:3 %>% `+`(4)
#[1] 5 6 7
2 |> setdiff(1:3, _)
#Error: pipe placeholder can only be used as a named argument
2 |> setdiff(1:3, y = _)
#[1] 1 3
2 |> (\(.) setdiff(1:3, .))()
#[1] 1 3
2 %>% setdiff(1:3, .)
#[1] 1 3
2 %>% setdiff(1:3, y = .)
#[1] 1 3
"b" |> paste("a", _, "c")
#Error: pipe placeholder can only be used as a named argument
"b" |> paste("a", . = _, "c")
#[1] "a b c"
"b" |> (\(.) paste("a", ., "c"))()
#[1] "a b c"
1:3 |> setNames(nm = _)
#1 2 3
#1 2 3
1:3 |> setNames(object = _, nm = _)
#Error in setNames(object = "_", nm = "_") :
# pipe placeholder may only appear once
1:3 |> (\(.) setNames(., .))()
#1 2 3
#1 2 3
1:3 |> list() |> setNames(".") |> with(setNames(., .))
#1 2 3
#1 2 3
1:3 |> list(. = _) |> with(setNames(., .))
#1 2 3
#1 2 3
1:3 %>% setNames(object = ., nm = .)
#1 2 3
#1 2 3
1:3 %>% setNames(., .)
#1 2 3
#1 2 3
1:3 |> sum(sqrt(x=_))
#Error in sum(1:3, sqrt(x = "_")) : invalid use of pipe placeholder
1:3 |> (\(.) sum(sqrt(.)))()
#[1] 4.146264
1:3 %>% {sum(sqrt(.))}
#[1] 4.146264
_
现在也可以在前向管道|>
表达式的rhs中作为提取调用的第一个参数使用,例如_$coef
。更一般地,它可以作为一系列提取的头部使用,例如_$coef[[2]]
。mtcars |> _$cyl
mtcars |> _[["cyl"]]
mtcars |> _[,"cyl"]
# [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
mtcars |> _$cyl[[4]]
#[1] 6
mtcars %>% .$cyl
mtcars %>% .[["cyl"]]
mtcars %>% .[,"cyl"]
# [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
#mtcars %>% .$cyl[4] #gives mtcars[[4]]
mtcars %>% .$cyl %>% .[4]
#[1] 6
assign("x", 1)
x
#[1] 1
"x" |> assign(2)
x
#[1] 2
"x" |> (\(x) assign(x, 3))()
x
#[1] 2
1:3 |> assign("x", value=_)
x
#[1] 1 2 3
"x" %>% assign(4)
x
#[1] 1 2 3
4 %>% assign("x", .)
x
#[1] 1 2 3
"x" %!>% assign(4) #Use instead the eager pipe
x
#[1] 4
5 %!>% assign("x", .)
x
#[1] 5
top6 <- . %>% sort() %>% tail()
top6(c(1:10,10:1))
#[1] 8 8 9 9 10 10
其他可能性:
可以通过使用奇异管道->.;
来实现不同的管道操作符和不同的占位符,这不是一个管道(参见缺点),它会覆盖.
1:3 ->.; sum(.)
#[1] 6
mtcars ->.; .$cyl
# [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
mtcars ->.; .$cyl[4]
#[1] 6
1:3 ->.; setNames(., .)
#1 2 3
#1 2 3
1:3 ->.; sum(sqrt(x=.))
#[1] 4.146264
"x" ->.; assign(., 5)
x
#[1] 5
6 ->.; assign("x", .)
x
#[1] 6
1:3 ->.; . + 4
#[1] 5 6 7
1 ->.; (`+`)(., 2) ->.; (`*`)(., 3)
#[1] 9
1 ->.; .+2 ->.; .*3
#[1] 9
x <- data.frame(a=0)
f1 <- \(x) {message("IN 1"); x$b <- 1; message("OUT 1"); x}
f2 <- \(x) {message("IN 2"); x$c <- 2; message("OUT 2"); x}
x ->.; f1(.) ->.; f2(.)
#IN 1
#OUT 1
#IN 2
#OUT 2
# a b c
#1 0 1 2
x |> f1() |> f2()
#IN 2
#IN 1
#OUT 1
#OUT 2
# a b c
#1 0 1 2
f2(f1(x))
#IN 2
#IN 1
#OUT 1
#OUT 2
# a b c
#1 0 1 2
`:=` <- \(lhs, rhs) eval(substitute(rhs), list(. = lhs))
mtcars := .$cyl[4]
#[1] 6
1:3 := setNames(., .)
#1 2 3
#1 2 3
1:3 := sum(sqrt(x=.))
#[1] 4.146264
"x" := assign(., 6)
x
#Error: object 'x' not found
1 := .+2 := .*3
#[1] 9
.
,并在调用环境中评估rhs。但是在这种情况下,如果.
已经存在于调用环境中,它将被移除。`?` <- \(lhs, rhs) {
on.exit(if(exists(".", parent.frame())) rm(., envir = parent.frame()))
assign(".", lhs, envir=parent.frame())
eval.parent(substitute(rhs))
}
mtcars ? .$cyl[4]
#[1] 6
1:3 ? setNames(., .)
#1 2 3
#1 2 3
1:3 ? sum(sqrt(x=.))
#[1] 4.146264
"x" ? assign(., 6)
x
#[1] 6
1 ? .+2 ? .*3
#[1] 9
.
替换为lhs,这样在评估过程中.
就不再存在作为一个名称。`%|>%` <- \(lhs, rhs)
eval.parent(eval(call('substitute', substitute(rhs), list(. = lhs))))
mtcars %|>% .$cyl[4]
[1] 6
1:3 %|>% setNames(., .)
1 2 3
1 2 3
1:3 %|>% sum(sqrt(x=.))
[1] 4.146264
"x" %|>% assign(., 6)
x
#[1] 6
1 %|>% .+2 %|>% .*3
#[1] 7
Speed
library(magrittr)
`:=` <- \(lhs, rhs) eval(substitute(rhs), list(. = lhs))
`?` <- \(lhs, rhs) {
on.exit(if(exists(".", parent.frame())) rm(., envir = parent.frame()))
assign(".", lhs, envir=parent.frame())
eval.parent(substitute(rhs))
}
`%|>%` <- \(lhs, rhs)
eval.parent(eval(call('substitute', substitute(rhs), list(. = lhs))))
x <- 42
bench::mark(min_time = 0.2, max_iterations = 1e8
, x
, identity(x)
, "|>" = x |> identity()
, "|> _" = x |> identity(x=_)
, "->.;" = {x ->.; identity(.)}
, "|> f()" = x |> (\(y) identity(y))()
, "%>%" = x %>% identity
, ":=" = x := identity(.)
, "list." = x |> list() |> setNames(".") |> with(identity(.))
, "%|>%" = x %|>% identity(.)
, "?" = x ? identity(.)
)
结果
expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc
<bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl> <int> <dbl>
1 x 31.08ns 48.2ns 19741120. 0B 7.46 2646587 1
2 identity(x) 491.04ns 553.09ns 1750116. 0B 27.0 323575 5
3 |> 497.91ns 548.08ns 1758553. 0B 27.3 322408 5
4 |> _ 506.87ns 568.92ns 1720374. 0B 26.9 320003 5
5 ->.; 725.03ns 786.04ns 1238488. 0B 21.2 233864 4
6 |> f() 972.07ns 1.03µs 929926. 0B 37.8 172288 7
7 %>% 2.76µs 3.05µs 315448. 0B 37.2 59361 7
8 := 3.02µs 3.35µs 288025. 0B 37.0 54561 7
9 list. 5.19µs 5.89µs 166721. 0B 36.8 31752 7
10 %|>% 6.01µs 6.86µs 143294. 0B 37.0 27076 7
11 ? 30.9µs 32.79µs 30074. 0B 31.3 5768 6
magrittr
管道相比也少了一次按键。 - Faustin Gashakamba1:3 |> list() |> setNames(".") |> with(setNames(., .))
可以写成 1:3 |> list(. = _) |> with(setNames(., .))
或者甚至是 1:3 |> setNames(nm = _)
。 - G. GrothendiecksetNames
时没有必要在两个地方都使用占位符。 - GKi.
占位符的等效物,因此以下内容在|>
中是不可能的。c("dogs", "cats", "rats") %>% grepl("at", .)
#[1] FALSE TRUE TRUE
_
作为占位符,但仅限于命名参数。c("dogs", "cats", "rats") |> grepl("at", x = _)
#[1] FALSE TRUE TRUE
.
和 magrittr 仍然更加灵活,因为 .
可以重复出现并且可以出现在表达式中。
c("dogs", "cats", "rats") %>%
paste(., ., toupper(.))
#[1] "dogs dogs DOGS" "cats cats CATS" "rats rats RATS"
c("dogs", "cats", "rats") |>
paste(x = "no", y = _)
# Error in paste(x = "_", y = "_") : pipe placeholder may only appear once
使用带有未命名可变参数的函数(即...
)与|>
的方法也不清楚。 在这个paste()
示例中,我们可以编造x
和y
参数来欺骗占位符放置在正确的位置,但这感觉很不专业。
c("dogs", "cats", "rats") |>
paste(x = "no", y = _)
#[1] "no dogs" "no cats" "no rats"
以下是绕过占位符限制的其他方法:
find_at = function(x) grepl("at", x)
c("dogs", "cats", "rats") |> find_at()
#[1] FALSE TRUE TRUE
Use an anonymous function
a) Use the "old" syntax
c("dogs", "cats", "rats") |> {function(x) grepl("at", x)}()
b) Use the new anonymous function syntax
c("dogs", "cats", "rats") |> {\(x) grepl("at", x)}()
Specify the first parameter by name. This relies on the fact that the native pipe pipes into the first unnamed parameter, so if you provide a name for the first parameter it "overflows" into the second (and so on if you specify more than one parameter by name)
c("dogs", "cats", "rats") |> grepl(pattern="at")
#> [1] FALSE TRUE TRUE
c("dogs", "cats", "rats") |> d => grepl("at", d)
。 - Wang|>
不像 %>%
一样允许使用 .
?这样会更简洁和好用。 - GitHunter0在 R 4.1.0 中新增的基础管道符 |>
只是函数组合。也就是说,我们可以看到它的使用实际上与函数调用完全相同:
> 1:5 |> sum() # simple use of |>
[1] 15
> deparse(substitute( 1:5 |> sum() ))
[1] "sum(1:5)"
>
这会产生一些影响:
sum()
需要括号进行正确调用这导致可能使用当前为“available but not active”的=>
(需要设置环境变量_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))"
>
deparse(substitute(...))
在这里特别好用。
.
?你所说的 =>
可能的用法是什么?pipebind 是否类似于 %<>%
? - qwr=>
将数据元素 显式地 分配给一个命名变量,比如 x
,并在 lm(...., data=x)
中使用它。 - Dirk Eddelbuettel=>
是做什么的,但我想这是另一个问题。 - qwr本地管道的实现是通过语法转换来实现的,因此2 |> sqrt()
与sqrt(2)
相比没有明显的开销,而2 %>% sqrt()
会带来一些小的惩罚。
microbenchmark::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
你看到了将表达式2 |> sqrt()
传递给microbenchmark
,它被解析为sqrt(2)
。这也可以在中看到。
quote(2 |> sqrt())
# sqrt(2)
一个区别是它们的占位符,基础R使用_
,而magrittr
使用.
。
自从 R 4.2.0版本开始,基础R管道有了一个占位符,_
,类似于 %>%
使用的.
,但其仅限用于命名参数,并且每次调用只能使用一次。
现在可以在rhs调用中使用带有占位符_的命名参数,以指定lhs应插入的位置。 占位符只能出现在rhs上一次。
重申Ronak Shah的例子,现在可以将_
用作右侧的命名参数,以引用公式左侧:
c("dogs", "cats", "rats") |>
grepl("at", x = _)
#[1] FALSE TRUE TRUE
但它必须被命名为:
c("dogs", "cats", "rats") |>
grepl("at", _)
#Error: pipe placeholder can only be used as a named argument
并且不能出现超过一次(为了解决这个问题,人们仍然可以使用Ronak Shah提供的解决方案):
c("dogs", "cats", "rats") |>
expand.grid(x = _, y = _)
# Error in expand.grid(x = "_", y = "_") : pipe placeholder may only appear once
虽然这是使用 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
|>
等)重写了语法,使得 longcalc() |> quux(x = _)
变成了 quux(x = longcalc())
,而他们不希望 longcalc() |> quux(x=_, y=)
被翻译成双倍计算的 quux(x=longcalc(), y=longcalc())
(其中第二个是多余和双倍时间调用)。只是一个猜测。@GitHunter0 - r2evans
?pipeOp
和?"%>%"
帮助页面了吗?那是一个很好的信息来源。 - MrFlick