6

With the magrittr pipe (%>%), I'd occasionally pipe the result to multiple parameters, like

ds <- 
  datasets::airquality |> 
  head() 

ds %>%
  # ds |> 
  knitr::kable(
    x           = .,
    col.names   = tolower(colnames(.)),
    format      = "markdown"
  )

result:

| ozone| solar.r| wind| temp| month| day|
|-----:|-------:|----:|----:|-----:|---:|
|    41|     190|  7.4|   67|     5|   1|
|    36|     118|  8.0|   72|     5|   2|
|    12|     149| 12.6|   74|     5|   3|
|    18|     313| 11.5|   62|     5|   4|
|    NA|      NA| 14.3|   56|     5|   5|
|    28|      NA| 14.9|   66|     5|   6|

But R's new native pipe (|>, introduced in 4.1.0) does not support this. Replacing %>% with |> throws this error:

Error in knitr::kable(head(datasets::airquality), x = ., col.names = tolower(colnames(.)),  : 
  object '.' not found

The description in the release notes (my emphasis):

R now provides a simple native forward pipe syntax |>. The simple form of the forward pipe inserts the left-hand side as the first argument in the right-hand side call. The pipe implementation as a syntax transformation was motivated by suggestions from Jim Hester and Lionel Henry.

Other than defining a new (anonymous or explicit) function that wraps the rhs (right-hand side) function proposed below, is there another approach using |>?

csgillespie
  • 59,189
  • 14
  • 150
  • 185
wibeasley
  • 5,000
  • 3
  • 34
  • 62
  • 1
    Have also a look at: [Pipe purely in base R ('base pipe')?](https://stackoverflow.com/q/65329335/10488504) – GKi Jun 29 '21 at 12:48

2 Answers2

4

Jumping Rivers's blog describes how to use an anonymous function and the new native pipe (a) to pass a value to a parameter that's not the first parameter and (b) to pass a value to multiple parameters. For the question above:

ds |>
  {\(x) 
    knitr::kable(
      x           = x,
      col.names   = tolower(colnames(x)),
      format      = "markdown"
    )
  }()                                      # Don't forget the parentheses.

This leverages the R 4.1.0 release feature.

R now provides a shorthand notation for creating functions, e.g. (x) x + 1 is parsed as function(x) x + 1.

It's a little less verbose than defining the function explicitly like

kable2 <- function (x) {
  knitr::kable(
    x           = x,
    col.names   = tolower(colnames(x)),
    format      = "markdown"
  )
}

ds |>
  kable2()

Note: if you receive the following error, you may have forgotten the () (i.e., opening & closing parentheses) after the anonymous function is defined.

Error: function '{' not supported in RHS call of a pipe

csgillespie
  • 59,189
  • 14
  • 150
  • 185
wibeasley
  • 5,000
  • 3
  • 34
  • 62
2

As explained by Keith McNulty in a recent Medium post, use of “.” reference has been removed in the new R native pipe. Instead, you have to use intermediate functions as you proposed but also take the advantage that native |> pipes into the first unnamed argument.

So, if you were not using col.names argument,

library(dplyr)
ds <- datasets::airquality |> head()

ds %>%
  knitr::kable(
    x           = .,
    format      = "markdown"
  )

could be replaced by

ds |>
  knitr::kable(
    format      = "markdown"
  )

as x is the first unnamed argument of kable

To deal with the second “.” reference, you can use first a function (dplyr::rename_with in this case)

ds |> 
  dplyr::rename_with(tolower) |>
  knitr::kable(
    format      = "markdown"
  )
| ozone | solar.r | wind | temp | month | day |
|------:|--------:|-----:|-----:|------:|----:|
|    41 |     190 |  7.4 |   67 |     5 |   1 |
|    36 |     118 |  8.0 |   72 |     5 |   2 |
|    12 |     149 | 12.6 |   74 |     5 |   3 |
|    18 |     313 | 11.5 |   62 |     5 |   4 |
|    NA |      NA | 14.3 |   56 |     5 |   5 |
|    28 |      NA | 14.9 |   66 |     5 |   6 |

Created on 2021-06-29 by the reprex package (v2.0.0)

josep maria porrà
  • 1,198
  • 10
  • 18
  • Thanks for pointing out that some transformations might be better placed before `kable()` (or whatever function needs two outputs from the pipe). For the record, the actual column-renaming function is a little more complicated than my example; I used `tolower()` because it's a little easier to understand than `gsub("_", " ", colnames(.))` – wibeasley Jun 29 '21 at 16:37