23 Pipes
23.1 Introduction
In its basic form, a pipe allows writing: f(x)
as x |> f()
and, similarly, g(f(x))
as x |> f() |> g()
.
A pipe operator was first introduced to R by the magrittr package with the %>%
symbol. Note that a number of other packages that endorse the use of pipes export the pipe operator as well.
Starting with R version 4.1 (May 2021), a native pipe operator is included with the |>
symbol.
A pipe allows writing f(x)
as x |> f()
(native pipe) or x %>% f
(magrittr).
Note that the native pipe requires parentheses, but magrittr works with or without them.
A pipe is often used to:
- avoid multiple temporary assignments in a multistep procedure, or
- as an alternative to nesting functions.
Some packages and developers promote its use, others discourage it. You should try and see if/when it suits your needs.
The following:
x <- f1(x)
x <- f2(x)
x <- f3(x)
is equivalent to:
x <- f3(f2(f1(x)))
is equivalent to:
x <- x |> f1() |> f2() |> f3()
$setosa
Sepal.Length Sepal.Width Petal.Length Petal.Width
5.006 3.428 1.462 0.246
$versicolor
Sepal.Length Sepal.Width Petal.Length Petal.Width
5.936 2.770 4.260 1.326
$virginica
Sepal.Length Sepal.Width Petal.Length Petal.Width
6.588 2.974 5.552 2.026
Pipes are used extensively in the tidyverse packages and many other third-party packages.
You can learn more about the magrittr pipe operator in the vignette.
In RStudio the keyboard shortcut for the pipe operator is Shift-Command-M
(MacOS) or Ctrl-Shift-M
(Windows).
23.2 Differences between native pipe and magrittr
Native pipe requires parenthesis (()
) after function name, magrittr works with or without them. For example,
x <- rnorm(300)
x |> mean()
[1] 0.01181341
but this would fail:
|> mean x
while either works in magrittr:
x %>% mean
[1] 0.01181341
The native pipe passes its LHS to the first unnamed argument on the RHS. Alternatively, it can be passed to any named argument using an underscore (“_“) symbol.
On the other hand, magrittr allows using a period .
to pipe to any position on the RHS. The native pipe workaround is using an anonymous function (can use the new shorter syntax \(x)
instead of function(x)
).
Example: Find the position of “r” in the latin alphabet
Here, we want to pass the LHS to the second argument of grep()
.
Using native pipe, we name the first argument pattern
and the LHS is passed to the first unnamed argument, i.e. the second (which is x
, the character vector where matches are looked for):
Native pipe or magrittr: pass to the first unnamed argument:
letters |> grep(pattern = "r")
[1] 18
Native pipe: pass to any named argument using an underscore (_
) placeholder, but argument must be named:
letters |> grep("r", x = _)
[1] 18
magrittr: pass to any argument using a period (.
) placeholder. Argument can be named or not (i.e. positional):
For demonstration purposes, here’s how you can achieve the same using the native pipe and an anonymous function.