[英]Why isn't the magrittr %<>% assignment pipe working with R's native pipe (|>)
[英]What are the differences between R's new native pipe `|>` and the magrittr pipe `%>%`?
在 R 4.1 中引入了一個本地管道運算符,它比以前的實現“更流線型”。 我已經注意到原生|>
和 magrittr 管道%>%
之間的一個區別,即2 %>% sqrt
有效,但2 |> sqrt
無效,必須寫成2 |> sqrt()
。 使用新的管道運算符時是否需要注意更多差異和陷阱?
它們兩者之間的另一個區別是管道輸入的值.
可以用作magrittr
的 pipe 中的占位符
c("dogs", "cats", "rats") %>% grepl("at", .)
#[1] FALSE TRUE TRUE
但這對於 R 的原生 pipe 是不可能的。
c("dogs", "cats", "rats") |> grepl("at", .)
grepl 錯誤(c("dogs", "cats", "rats"), "at", .): object '.' 未找到
以下是引用它們的不同方法 -
find_at = function(x) grepl("at", x)
c("dogs", "cats", "rats") |> find_at()
#[1] FALSE TRUE TRUE
使用匿名 function
a) 使用“舊”語法
c("dogs", "cats", "rats") |> {function(x) grepl("at", x)}()
b) 使用新的匿名 function 語法
c("dogs", "cats", "rats") |> {\(x) grepl("at", x)}()
按名稱指定第一個參數。 這取決於本機 pipe 管道進入第一個未命名參數的事實,因此如果您為第一個參數提供名稱,它會“溢出”到第二個參數(如果您按名稱指定多個參數,依此類推)
c("dogs", "cats", "rats") |> grepl(pattern="at")
#> [1] FALSE TRUE TRUE
基礎 R pipe |>
添加到 R 4.1.0 中“只是”進行功能組合。 即我們可以看到它的使用真的和函數調用一樣:
> 1:5 |> sum() # simple use of |>
[1] 15
> deparse(substitute( 1:5 |> sum() ))
[1] "sum(1:5)"
>
這有一些后果:
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))"
>
deparse(substitute(...))
在這里特別好。
本機 pipe 是作為語法轉換實現的,因此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
您會看到傳遞給microbenchmark
的表達式2 |> sqrt()
是如何被解析為sqrt(2)
的。 這也可以在
quote(2 |> sqrt())
# sqrt(2)
話題 | 馬格利特2.0.3 | 基礎4.2.0 |
---|---|---|
操作員 | %>% |
|> |
Function 來電 | %>% sum() |
|> sum() |
%>% sum |
需要括號 | |
%>% `$`(cyl) |
不支持某些功能 | |
占位符 | . |
_ |
%>% lm(mpg ~ disp, data =. ) |
|> lm(mpg ~ disp, data = _ ) |
|
%>% lm(mpg ~ disp, . ) |
需要命名參數 | |
%>% setNames(., .) |
只能出現一次 | |
%>% {sum(sqrt(.))} |
不允許嵌套調用 | |
環境 | 附加 function 環境 | "x" |> assign(1) |
速度 | function 調用的開銷 | 語法轉換 |
將|>
與(匿名)function: 1 |> (\(.).)()
, -3:3 |> (\(.) sum(2*abs(.) - 3*.^2))()
需要括號
library(magrittr)
1:3 |> sum
#Error: The pipe operator requires a function call as RHS
1:3 |> sum()
#[1] 6
1:3 %>% sum
#[1] 6
1:3 %>% sum()
#[1] 6
不支持某些函數,但仍然可以通過將它們放在括號中來調用它們,通過 function ::
調用它們,在 function 中調用它或定義到 ZC1C425268E68385D1AB5074F 的鏈接。
mtcars |> `$`(cyl)
#Error: function '$' not supported in RHS call of a pipe
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 |> base::`$`(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)()
# [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
fun <- `$`
mtcars |> fun(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)
# [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
占位符需要命名參數
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
占位符只能出現一次
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 %>% 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
沒有額外的環境
assign("x", 1)
x
#[1] 1
"x" |> assign(2)
x
#[1] 2
"x" |> (\(x) assign(x, 3))()
x
#[1] 2
"x" %>% assign(4)
x
#[1] 2
其他可能性:
使用 Bizarro pipe ->.;
可以實現不同的 pipe 運算符和不同的占位符。 什么不是正在覆蓋的 pipe(請參閱缺點) .
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
1:3 ->.; setNames(., .)
#1 2 3
#1 2 3
1:3 ->.; sum(sqrt(x=.))
#[1] 4.146264
"x" ->.; assign(., 5)
x
#[1] 5
並且評價不同。
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
或者定義一個自己的運算符,它評估不同。
":=" <- function(lhs, rhs) {
e <- exists(".", parent.frame(), inherits = FALSE)
. <- get0(".", envir = parent.frame(), inherits = FALSE)
assign(".", lhs, envir=parent.frame())
on.exit(if(identical(lhs, get0(".", envir = parent.frame(), inherits = FALSE))) {
if(e) {
assign(".", ., envir=parent.frame())
} else {
if(exists(".", parent.frame())) rm(., envir = parent.frame())
}
})
eval(substitute(rhs), parent.frame())
}
. <- 0
"." := assign(., 1)
.
#[1] 1
1:3 := sum(.)
#[1] 6
.
#[1] 1
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
1:3 := setNames(., .)
#1 2 3
#1 2 3
1:3 := sum(sqrt(x=.))
#[1] 4.146264
"x" := assign(., 6)
x
#[1] 6
1 := .+1 := .+2
#[1] 4
x <- data.frame(a=0)
x := f1(.) := f2(.)
#IN 1
#OUT 1
#IN 2
#OUT 2
# a b c
#1 0 1 2
速度
library(magrittr)
":=" <- function(lhs, rhs) {
e <- exists(".", parent.frame(), inherits = FALSE)
. <- get0(".", envir = parent.frame(), inherits = FALSE)
assign(".", lhs, envir=parent.frame())
on.exit(if(identical(lhs, get0(".", envir = parent.frame(), inherits = FALSE))) {
if(e) {
assign(".", ., envir=parent.frame())
} else {
if(exists(".", parent.frame())) rm(., envir = parent.frame())
}
})
eval(substitute(rhs), parent.frame())
}
`%|%` <- function(lhs, rhs) { #Overwrite and keep .
assign(".", lhs, envir=parent.frame())
eval(substitute(rhs), parent.frame())
}
x <- 42
bench::mark(min_time = 0.2, max_iterations = 1e8
, x
, identity(x)
, "|>" = x |> identity()
, "|> _" = x |> identity(x=_)
, "|> f()" = x |> (\(y) identity(y))()
, "%>%" = x %>% identity
, "->.;" = {x ->.; identity(.)}
, ":=" = x := identity(.)
, "%|%" = x %|% identity(.)
, "list." = x |> list() |> setNames(".") |> with(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 9.89ns 10.94ns 66611556. 0B 11.7 5708404 1
# 2 identity(x) 179.98ns 200.12ns 4272195. 0B 49.6 603146 7
# 3 |> 179.98ns 201.05ns 4238021. 0B 41.1 722534 7
# 4 |> _ 189.87ns 219.91ns 4067314. 0B 39.4 722803 7
# 5 |> f() 410.01ns 451.11ns 1889295. 0B 44.6 339126 8
# 6 %>% 1.27µs 1.39µs 632255. 5.15KB 43.2 117210 8
# 7 ->.; 289.87ns 330.97ns 2581693. 0B 27.0 477389 5
# 8 := 6.46µs 7.12µs 131921. 0B 48.8 24330 9
# 9 %|% 2.05µs 2.32µs 394515. 0B 43.2 73094 8
#10 list. 2.42µs 2.74µs 340220. 8.3KB 42.3 64324 8
一個區別是它們的占位符_
在基礎 R, 中.
在magrittr
。
由於R 4.2.0 ,基礎 R pipe 有一個用於管道輸入值的占位符_
,類似於%>%
.
,但其使用僅限於命名為 arguments,並且每次調用只能使用一次。
現在可以在 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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.