26

I frequently use the dplyr piping to get a column from a tibble into a vector as below

iris %>% .$Sepal.Length
iris %>% .$Sepal.Length %>% cut(5)

How can I do the same using the latest R built-in pipe symbol |>

iris |> .$Sepal.Length
iris |> .$Sepal.Length |>  cut(5)
Error: function '$' not supported in RHS call of a pipe
jay.sf
  • 60,139
  • 8
  • 53
  • 110
Afiq Johari
  • 1,372
  • 1
  • 15
  • 28

7 Answers7

21

We can use getElement().

iris |> getElement('Sepal.Length') |> cut(5)
jay.sf
  • 60,139
  • 8
  • 53
  • 110
17

In base pipe no placeholder is provided for the data that is passed in the pipe. This is one difference between magrittr pipe and base R pipe. You may use an anonymous function to access the object.

iris |> {\(x) x$Sepal.Length}()
Ronak Shah
  • 377,200
  • 20
  • 156
  • 213
  • 1
    I see, that's longer that I expected, but great to know. Thanks @Ronak – Afiq Johari Jun 02 '21 at 06:28
  • Is wrapping in parethesis iris |> (\(x) x$Sepal.Length)() |> cut(5) like this is the same as using {}? – jpdugo17 Jun 02 '21 at 06:45
  • I have not used parenthesis like this but I think it works the same way. – Ronak Shah Jun 02 '21 at 06:51
  • 3
    @jpdugo17 The only differences are that `{}` can contain multiple expressions (separated by newline or, `;`), and that it preserves a value's auto-printing visibility. … And of course it uses visually distinct delimiters, which can make nested expressions (as in this case) more readable. – Konrad Rudolph Jun 02 '21 at 20:28
11

Since R 4.3.0, as an experimental feature, the placeholder _ can now also be used in the rhs of a forward pipe |> expression as the first argument in an extraction call, such as _$coef. More generally, it can be used as the head of a chain of extractions, such as _$coef[[2]].

iris |> _$Sepal.Length

iris |>  _[["Sepal.Length"]]

For more then one column:

iris |> _[c("Sepal.Length", "Petal.Length")]

Before 4.3.0 was the direct usage of $ in |> disabled. If the call of $ or other disabled functions in |> are still needed, an option, beside the creation of a function is to use $ via the function :: as base::`$` or place it in brakes ($):

iris |> (`$`)("Sepal.Length")

iris |> base::`$`("Sepal.Length")

iris |> (\(.) .$Sepal.Length)()

fun <- `$`
iris |> fun(Sepal.Length)

This will also work in cases where more than one column will be extracted.

iris |> (`[`)(c("Sepal.Length", "Petal.Length"))

Another option can be the use of a bizarro pipe ->.;. Some call it a joke others clever use of existing syntax.

iris ->.; .$Sepal.Length

This creates or overwrites . in the .GlobalEnv. rm(.) can be used to remove it. Alternatively it could be processed in local:

local({iris ->.; .$Sepal.Length})

In this case it produces two same objects in the environment iris and . but as long as they are not modified they point the the same address.

tracemem(iris)
#[1] "<0x556871bab148>"
tracemem(.)
#[1] "<0x556871bab148>"
GKi
  • 37,245
  • 2
  • 26
  • 48
  • 1
    To clarify, now we have 2 same objects in the environment `iris` and `.` – zx8754 Jun 02 '21 at 07:49
  • Great, but the Bizzarro Pipe article states that though this works, it was intended as a "joke" and should not be used in practice. – tpetzoldt Jun 02 '21 at 07:59
  • 2
    @tpetzoldt At least *g-grothendieck* describes it as [clever use of existing syntax](https://stackoverflow.com/a/65329845/10488504) – GKi Jun 02 '21 at 08:43
  • @zx8754 Thanks for the comment. I have added that these objects *point the the same address as long as they are not modified*. – GKi Jun 02 '21 at 10:06
  • 4
    @GKi “clever” ≠ “good”. I hope nobody *actually* recommends using this in practice. – Konrad Rudolph Jun 02 '21 at 20:40
  • @KonradRudolph Thanks for the comment. But why should it not be recommended? I asked a [question on this topoc](https://stackoverflow.com/q/67868289/10488504). – GKi Jun 07 '21 at 08:29
5

Interesting example and great answers, let me add another version: I use usually selectand then unlist in such cases. This follows the "speaking R" paradigm and works same with both operators %>% and |>:

library("dplyr")
iris %>% select(Sepal.Length) %>% unlist() %>% cut(5)

iris |> select(Sepal.Length) |> unlist() |> cut(5)

Note that select is from dplyr and pull brought in from @jpdugo17 is even better.

If we use usual "base R" indexing, it is also short and works in both worlds:

iris[["Sepal.Length"]] |> cut(5)

iris$Sepal.Length |> cut(5)

and thanks to the comment of @zx8754 one can of course also use base R without any pipes

cut(iris$Sepal.Length, 5)

... but I think that the OP just wanted to point out differences in piping. I guess that it is to be applied in a bigger context and iris is only an example.

tpetzoldt
  • 5,338
  • 2
  • 12
  • 29
  • 1
    iris |> subset(TRUE, Sepal.Length) |> unlist() |> cut(5) also does the trick while remaining in base R. – jpdugo17 Jun 02 '21 at 16:07
  • Hmm. Honestly I can’t see the benefit of the `select(…) |> unlist()` idiom. It’s strictly more verbose than using `with(…)` or `pull(…)`. What am I missing? – Konrad Rudolph Jun 02 '21 at 20:39
  • @KonradRudolph It's just in case that for whatever reason only base functions are available. And because tidyverse already has magrittr's pipe that in my opinion it's much better and preferred. – jpdugo17 Jun 02 '21 at 21:07
  • @jpdugo17 Well in that case just use `with`. – Konrad Rudolph Jun 03 '21 at 07:56
  • `with` is of course fine, but as I understood the OP, the question was mainly motivated by exploring differences between `%>%` and `|>`. My personal impression is, that this is highly needed to gain more understanding how to use pipes in the future. – tpetzoldt Jun 03 '21 at 08:51
4

This is also an option:

iris |> dplyr::pull(Sepal.Length) |> cut(5)

Edit:

I wonder why calling a function with backticks isn't allowed.

iris |> `[`(, 'Sepal.Length')
#>Error: function '[' not supported in RHS call of a pipe 

As pointed out by @Hugh, backticks are allowed but some functions are not.

Here's the blacklisted functions list extracted from wch Github

"if", "while", "repeat", "for", "break", "next", "return", "function",
"(", "{",
"+", "-", "*", "/", "^", "%%", "%/%", "%*%", ":", "::", ":::", "?", "|>",
"~", "@", "=>",
"==", "!=", "<", ">", "<=", ">=",
"&", "|", "&&", "||", "!",
"<-", "<<-", "=",
"$", "[", "[[",
"$<-", "[<-", "[[<-",
0
jpdugo17
  • 6,816
  • 2
  • 11
  • 23
  • If we can use pull from dplyr why not pipe? The question suggests a case working in baseR only – AnilGoyal Jun 02 '21 at 08:29
  • @jpdugo17 thanks for this answer, I can see myself using this solution as well because I want to avoid using the `'...'` often. ButI think the `getElement()` solution is closer to pure `baseR` solution. Thanks again – Afiq Johari Jun 02 '21 at 08:40
  • 1
    It's not the backtick, but the `[` operator that is blacklisted. You can see the blacklist in names.c : https://github.com/wch/r-source/blob/e3b297a64f932d402dc59727c088386cae6a8112/src/main/names.c#L1023 – Hugh Jun 02 '21 at 09:37
  • 1
    @Hugh The fact that the pipe code prohibits using these names is increeeedibly annoying, and doesn’t seem to have any good reason. – Konrad Rudolph Jun 02 '21 at 20:38
  • 1
    @KonradRudolph there are good reasons but they’re not easy to explain. See Luke’s discussion on the mailing list – Hugh Jun 03 '21 at 02:25
  • @Hugh Actually I can’t find that explanation. I’m *fairly* sure I’ve read all relevant threads. There’s one where Luke says it’s important to remove ambiguity but then [later](https://stat.ethz.ch/pipermail/r-devel/2020-December/080213.html) he walks back on this. In particular, *there’s simply no ambiguity* when requiring trailing parentheses after a function call (as is currently thankfully the case). I’ve been using a custom pipe operator since before ‘magrittr’ existed and I’ve spent a lot of time thinking about implementations and I can’t think of a valid reason for this behaviour. – Konrad Rudolph Jun 03 '21 at 09:11
  • (Of course claiming I have experience in the subject doesn’t make me immune from mistakes. It’s entirely possible that there’s a compelling reason I’m overlooking. But I can’t find that reason on r-devel.) – Konrad Rudolph Jun 03 '21 at 09:14
1

I know this question is closed. Other Base R solutions where we use symbol name instead of the character name might include:

 iris |>
    with(Sepal.Length)


 iris |>
    subset(select = Sepal.Length)
Onyambu
  • 67,392
  • 3
  • 24
  • 53
1

Since R 4.2.0, you can use _ as a placeholder for |>. Because "functions in rhs calls [can] not be syntactically special", you cannot use $ directly, so you have to define the function with another name first, and then use the placeholder and the column name:

set <- `$`
iris |> set(x = _, Sepal.Length)
Maël
  • 45,206
  • 3
  • 29
  • 67