R的新本机管道有什么不同?|>'和马格里管' %>%'?

gudnpqoy  于 2023-02-27  发布在  其他
关注(0)|答案(5)|浏览(329)

在R4.1中,引入了一个原生管道操作符,它比以前的实现“更精简”。我已经注意到原生|>和magrittr管道%>%之间的一个区别,即2 %>% sqrt可以工作,但2 |> sqrt不能工作,必须写成2 |> sqrt()。在使用新管道操作符时,是否还有更多的区别和陷阱需要注意?

zwghvu4y

zwghvu4y1#

在R4.1中,没有本地管道的占位符语法,因此,不存在magrittr的.占位符的等价物,因此以下内容不可能在|>中执行。

c("dogs", "cats", "rats") %>% grepl("at", .)
#[1] FALSE  TRUE  TRUE

从R 4.2开始,原生管道可以使用_作为占位符,但 * 只能使用命名参数 *。

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()示例中,我们可以构造xy参数,以将占位符骗到正确的位置,但这感觉有点笨拙。

c("dogs", "cats", "rats") |>
  paste(x = "no", y = _) 
#[1] "no dogs" "no cats" "no rats"

以下是解决保持器限制的其他方法-
1.编写单独的函数

find_at = function(x) grepl("at", x)
c("dogs", "cats", "rats") |> find_at()
#[1] FALSE  TRUE  TRUE

1.使用匿名函数
a)使用“旧”语法

c("dogs", "cats", "rats") |> {function(x) grepl("at", x)}()

B)使用 new 匿名函数语法

c("dogs", "cats", "rats") |> {\(x) grepl("at", x)}()

1.按名称指定第一个参数。这依赖于本地管道连接到第一个 * 未命名 * 参数的事实,因此如果您为第一个参数提供名称,它将“溢出”到第二个参数(如果按名称指定多个参数,则依此类推)

c("dogs", "cats", "rats") |> grepl(pattern="at")
#> [1] FALSE  TRUE  TRUE
xxe27gdn

xxe27gdn2#

在R 4.1.0中添加的基本R管道|>“只是”进行函数组合。也就是说,我们可以看到它的使用 really 与函数调用是一样的:

> 1:5 |> sum()             # simple use of |>
[1] 15
> deparse(substitute( 1:5 |> sum() ))
[1] "sum(1:5)"
>

这产生了一些后果:

  • 它会让速度快一点
  • 它使它更简单更健壮
  • 它使它更有限制性:sum()在这里需要括号才能正确调用
  • 它限制了“implicit”数据参数的使用

这可能导致使用当前“可用但未激活”的=>(您需要为此设置环境变量_R_USE_PIPEBIND_,并且在R 4.2.0中可能会更改)。
(This第一次提供是作为复制这个over here的问题的答案,我只是按照建议复制了它。)

  • 编辑:* 由于后续问题“什么是=>”出现,这里是一个快速的后续问题。* 请注意,此操作符可能会更改。*
> 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(...))在这里特别好。

lfapxunr

lfapxunr3#

| 题目|马格利特 * 2.0.3 |基础 * 4.2.0 |
| - ------|- ------|- ------|
| * * 操作员
|%>%||>|
| * * 函数调用**|1:3 %>% sum()|1:3 |> sum()|
| | 1:3 %>% sum|* 需要括号 |
| | mtcars %>%$(cyl)|
不支持某些功能 |
| 在第一个空白位置插入|mtcars %>% lm(formula = mpg ~ disp)|mtcars |> lm(formula = mpg ~ disp)|
| * * 占位符
*|.|_|
| | mtcars %>% lm(mpg ~ disp, data = . )|x1米11米1x|
| | mtcars %>% lm(mpg ~ disp, . )|* 需要命名参数 |
| | 1:3 %>% setNames(., .)|
只能出现一次 |
| | 1:3 %>% {sum(sqrt(.))}|
不允许嵌套调用 |
| | mtcars %>% .$cyl|
管道占位符的使用无效 但在这个例子中mtcars$cyl|
| * * 环境
*|* 附加功能环境 |"x" |> assign(1)|
| * * 速度
*|较慢,因为 * 函数调用的开销 *|更快,因为 * 语法转换 *|
|>与(匿名)函数结合使用时,许多差异和限制都消失了:
1 |> (\(.) .)()
x1米20英寸1x

    • 需要括号**
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

有些函数*不受支持,但有些仍然可以通过将其放在括号中、通过函数::调用、在函数中调用或定义到函数的链接来调用。

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

同样,对于带有...(点-点-点)参数的变元函数,占位符_需要用作命名参数。

"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(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管道->.;实现不同的管道操作符和不同的占位符,但该管道不是管道(请参见disadvantages),它将覆盖.

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
wztqucjr

wztqucjr4#

原生管道是作为语法转换实现的,因此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

您可以看到传递给microbenchmark的表达式2 |> sqrt()如何被解析为sqrt(2)

quote(2 |> sqrt())
# sqrt(2)
omtl5h9j

omtl5h9j5#

一个区别是它们的占位符,_在碱基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

相关问题