如何在基础R中使用纯管道操作('base pipe')?

10

有没有一种方法可以在base R中进行管道操作,而不必定义您自己的函数(即“开箱即用”),也不必加载任何外部包?

也就是说,是否有(基础R)替代magrittr管道%>% 的方法?可能会在以后的R版本中出现吗?

这个功能在R 4.0.3中是否可用?如果不是,在哪个R版本中可以找到它?如果可能,如何实现?


5
基于 R 的 |> 运算符据说正在开发中。请查看useR!主题演讲 - Ian Campbell
2
核心R开发人员正在开发|>作为基本的R管道运算符。 它将类似于magrittr的%>%。 我不知道它的预期发布日期。 - r2evans
1
现在基本的 R 管道已经在 R 的开发版本中了。 - Andrew
@r2evans,|>并不是那么相似。它不支持占位符(您必须创建一个匿名函数),右侧带有一个参数的函数必须写成f()。不支持没有括号的f。 - G. Grothendieck
1
你说过不用定义自己的函数,而是作为注释。如果你始终使用显式的 . ,那么这个方法可以很好地工作:'%>%' <- function (lhs, rhs) eval(substitute(rhs), envir = list(. = lhs), enclos = parent.frame()) ,如 iris %>% head(.) - moodymudskipper
1
Related - Maël
3个回答

20
在 R 中,|> 被用作管道操作符。(自 4.1.0 版本起)
左侧表达式 lhs 被插入到右侧表达式 rhs 的调用中作为第一个自由参数
mtcars |> head()                      # same as head(mtcars)
#                   mpg cyl disp  hp drat    wt  qsec vs am gear carb
#Mazda RX4         21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
#Mazda RX4 Wag     21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
#Datsun 710        22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
#Hornet 4 Drive    21.4   6  258 110 3.08 3.215 19.44  1  0    3    1
#Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2
#Valiant           18.1   6  225 105 2.76 3.460 20.22  1  0    3    1

mtcars |> head(2)                     # same as head(mtcars, 2)
#              mpg cyl disp  hp drat    wt  qsec vs am gear carb
#Mazda RX4      21   6  160 110  3.9 2.620 16.46  0  1    4    4
#Mazda RX4 Wag  21   6  160 110  3.9 2.875 17.02  0  1    4    4

还可以使用命名参数和占位符_rhs调用中指定lhs要插入的位置。占位符只能出现一次rhs上。(自4.2.0起)

mtcars |> lm(mpg ~ disp, data = _)
#mtcars |> lm(mpg ~ disp, _)  #Error: pipe placeholder can only be used as a named argument
#Call:
#lm(formula = mpg ~ disp, data = mtcars)
#
#Coefficients:
#(Intercept)         disp  
#   29.59985     -0.04122  

或者在“one”之前明确命名参数:

mtcars |> lm(formula = mpg ~ disp)

如果占位符被多次使用或者作为任意位置上的命名或未命名参数,或者用于禁用函数:请使用(匿名)函数

mtcars |> (\(.) .[.$cyl == 6,])()
#mtcars ->.; .[.$cyl == 6,]           # Alternative using bizarro pipe
#local(mtcars ->.; .[.$cyl == 6,])    # Without overwriting and keeping .
#                mpg cyl  disp  hp drat    wt  qsec vs am gear carb
#Mazda RX4      21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
#Mazda RX4 Wag  21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
#Hornet 4 Drive 21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1
#Valiant        18.1   6 225.0 105 2.76 3.460 20.22  1  0    3    1
#Merc 280       19.2   6 167.6 123 3.92 3.440 18.30  1  0    4    4
#Merc 280C      17.8   6 167.6 123 3.92 3.440 18.90  1  0    4    4
#Ferrari Dino   19.7   6 145.0 175 3.62 2.770 15.50  0  1    5    6

mtcars |> (\(.) lm(mpg ~ disp, .))()
#Call:
#lm(formula = mpg ~ disp, data = .)
#
#Coefficients:
#(Intercept)         disp  
#   29.59985     -0.04122

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 

#The same but over a function
._ <- \(data, expr, ...) {
  eval(substitute(expr), list(. = data), enclos = parent.frame())
}
1:3 |> ._(setNames(., .))
#1 2 3 
#1 2 3 

一些函数是禁用的。

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)()
#[1] 5 6 7

fun <- `+`
1:3 |> fun(4)
#[1] 5 6 7

表达式 x |> f(y) 被解析为 f(x, y)。虽然管道中的代码按顺序编写,但仍遵循正常的 R 语言求值语义。因此,管道表达式只在右侧表达式中首次使用时被评估。

-1 |> sqrt() |> (\(x) 0)()
#[1] 0

. <- -1
. <- sqrt(.)
#Warning message:
#In sqrt(.) : NaNs produced
(\(x) 0)(.)
#[1] 0


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 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

. <- x
. <- f1(.)
#IN 1
#OUT 1
f2(.)
#IN 2
#OUT 2
#  a b c
#1 0 1 2

2
期待尝试!与magrittr管道有什么明显的区别吗?它是否使用“.”来表示传递的参数占位符? - stevec
1
lambda函数 \\(x) 也应该是可用的:mtcars |> subset(cyl == 4) |> (\\(d) lm(mpg ~ disp, data = d))() - Waldi
@stevec 你可以使用 |> . => 来创建一个占位符(请参见答案中的示例),但这需要显式激活,这可能会在未来发生变化。 - GKi
目前,不应再鼓励使用=>,因为根据Luke Tierney的说法,它很可能会被删除。 - shs
虽然很好,但在 R 4.1.2 中,运行第二个例子时,使用 data = _ 后,我得到了 Error: unexpected input in "mtcars |> lm(mpg ~ disp, data = _" 的错误。因此,在这个最新版本的 R 中似乎不支持下划线。 - MS Berends
2
啊,所以 R 4.2.0 的文档(仅在3周前发布)中指出:“在前向管道 |> 表达式中,现在可以使用带有占位符 _ 的命名参数来指定 lhs 要插入的 rhs 调用的位置。占位符只能在 rhs 上出现一次。”你应该在回答中说明它需要非常最新和最近发布的 R 版本。 - MS Berends

14

您可以使用bizarro pipe (还有这个),它只是对现有语法的巧妙运用,不需要其他函数或包,例如:

 mtcars ->.;
  transform(., mpg = 2 * mpg) ->.;   # change units
  lm(mpg ~., .) ->.;
  coef(.)

->.;看起来有点像一根管道。


8
截至撰写本答案时,R(4.0.3)的发布版本不包括管道运算符。
然而,正如在useR! 2020主题演讲中所指出的那样,基本的|>运算符正在开发中。
从2020年12月15日的R-devel 每日源代码pipeOp手册页中:
引用块:

管道表达式将lhs表达式的结果传递或管道到rhs表达式。

引用块:

如果rhs表达式是一个调用,则lhs被插入为调用中的第一个参数。因此,x |> f(y)被解释为f(x,y)。为避免歧义,在rhs调用中的函数可能不是语法上的特殊字符,例如+if

如果rhs表达式是一个函数表达式,则该函数将使用lhs值作为其参数进行调用。当lhs需要作为rhs调用中的第一个参数以外的参数传递时,这非常有用。
目前还不确定此运算符何时将进入发布版本,或者在发布之前是否会更改。

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