Pipes with magrittr

Prerequisites

The %>% comes fromt he magrittr package by Stefan Milton Bache. It is included in tidyverse. But if we wanted to focuse entirely on %>%, we can load magrittr explicitly.

library(magrittr)

Piping Alternatives

Intermediate steps

The simplest approach is to save each step as a new object. But the downside is that it forces you to name each intermediate element. This leads to cluttered code and the need to increment the suffix on each line

Object_size gives the memory occupied by all of its arguments. The results show that since the two objects have similar columns, they wont be copied over.

library(tidyverse)
library(dplyr)
library(pryr)
diamonds <- ggplot2::diamonds
diamonds2 <-  diamonds %>%
  mutate(pricepercarat = price / carat)
object_size(diamonds)
3.46 MB
object_size(diamonds2)
3.89 MB
object_size(diamonds2, diamonds)
3.89 MB

When you modify one of them, then that variable can no longer be shared between the two data frames and a copy must be made. The size of each data frame is unchanged, but the collecti size increases:

diamonds$carat[1] <-  NA
object_size(diamonds)
3.46 MB
object_size(diamonds2)
3.89 MB
object_size(diamonds2, diamonds)
4.32 MB

Overwrite the original

Instead of creating intermediat eobjects at each stpe, we could overwrite the original object. This is less typing and less mistakes, however, debugging is painful. The repetition of the object being transformed obscures what’s chagneing on each line.

Function composition

Just string the functions calls together. The disadvantage is that you have to read from inside out. From right to left and that the arguments end up spread far apart. The dagwood sandwich problem.

Use the pipe

The pipe works by performing a lexical transformation behind the scenes. Magrittr reassembes the code in the pipe to a form that works by overwriting an intermediate object. Pipe won’t work for two classes of functions:

functions that use the current environment

For example, assign() will create a new variable with the given name in the current enivronment:

assign("x",10)
x
[1] 10
"x" %>% assign(100)
x
[1] 10

The use of assign with the pipe does not work because it assigns it to a temporary environment used by %>%. If you do want to use assign with the pipe, you must be explicit about the environment:

env <- environment()
"x" %>% assign(100, envir = env)
x
[1] 100

Other functions include: get() and load().

Functions that use lazy evaluation

In R function arguments are only computed when the function uses them, not prior to calling the function. The pipe computes each element in turn, so you can’t rely on this behavior. One place that this is a problem is tryCatch() which lets you capture and handle errors.

tryCatch(stop("!"), error = function(e) "An error")
[1] "An error"
stop("!") %>%
  tryCatch(error = function(e) "An error")
Error in eval(expr, envir, enclos) : !

There are relatively wide class of functions with this behavior, including try(), suppressMessages(), and suppressWarnngs() in base R

When Not to use Pipe

Your pipes are longer than 10 steps
You have mulitple inputs or outputs.
You are thinking about a directed graph with a complex dependency structure. Pipes are fundamentally linear and expressing complex relationships with pipes will yield confusing codes

Other Magrittr functions

T pipe. Works like %>% but returns the left hand side results.

rnorm(100) %>%
  matrix(ncol = 2) %>%
  plot()%>%
  str()
 NULL

rnorm(100) %>%
  matrix(ncol = 2) %T>%
  plot()%>%
  str()
 num [1:50, 1:2] 0.3168 -0.1113 -1.6274 -0.0535 0.6482 ...

If you are working with functions that don’t have a data frame based API (ie. you pass them individual vectors, not a data frame and expressions to be evaluate din the context of that data frame) you might find the %$% useful. It explodes out the variables in a data frame so that you can refer to them explicitly. This is useful when working with many functions in base R:

library(magrittr)

Attaching package: <U+393C><U+3E31>magrittr<U+393C><U+3E32>

The following object is masked from <U+393C><U+3E31>package:purrr<U+393C><U+3E32>:

    set_names

The following object is masked from <U+393C><U+3E31>package:tidyr<U+393C><U+3E32>:

    extract
mtcars %$%
  cor(disp, mpg)
[1] -0.8475514

For assignment magrittr provides the %<>% operator which allows you to replace code like this:

mtcars <-  mtcars %>%
  transform(cyl = cyl *2)
mtcars

with this:

mtcars %<>% transform(cyl = cyl *2)
mtcars
LS0tDQp0aXRsZTogIlIgRm9yIERhdGEgU2NpZW5jZSBDaGFwdGVyIDE0Ig0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KPGgxPiBQaXBlcyB3aXRoIG1hZ3JpdHRyIDxoMT4NCg0KPGgyPiBQcmVyZXF1aXNpdGVzIDwvaDI+DQpUaGUgJT4lIGNvbWVzIGZyb210IGhlIG1hZ3JpdHRyIHBhY2thZ2UgYnkgU3RlZmFuIE1pbHRvbiBCYWNoZS4gSXQgaXMgaW5jbHVkZWQgaW4gdGlkeXZlcnNlLiBCdXQgaWYgd2Ugd2FudGVkIHRvIGZvY3VzZSBlbnRpcmVseSBvbiAlPiUsIHdlIGNhbiBsb2FkIG1hZ3JpdHRyIGV4cGxpY2l0bHkuIA0KDQpsaWJyYXJ5KG1hZ3JpdHRyKSA8L2JyPg0KDQo8aDI+IFBpcGluZyBBbHRlcm5hdGl2ZXMgPC9oMj4NCjxoMz5JbnRlcm1lZGlhdGUgc3RlcHM8L2gzPg0KDQpUaGUgc2ltcGxlc3QgYXBwcm9hY2ggaXMgdG8gc2F2ZSBlYWNoIHN0ZXAgYXMgYSBuZXcgb2JqZWN0LiBCdXQgdGhlIGRvd25zaWRlIGlzIHRoYXQgaXQgZm9yY2VzIHlvdSB0byBuYW1lIGVhY2ggaW50ZXJtZWRpYXRlIGVsZW1lbnQuIFRoaXMgbGVhZHMgdG8gY2x1dHRlcmVkIGNvZGUgYW5kIHRoZSBuZWVkIHRvIGluY3JlbWVudCB0aGUgc3VmZml4IG9uIGVhY2ggbGluZSA8L2JyPg0KDQpPYmplY3Rfc2l6ZSBnaXZlcyB0aGUgbWVtb3J5IG9jY3VwaWVkIGJ5IGFsbCBvZiBpdHMgYXJndW1lbnRzLiBUaGUgcmVzdWx0cyBzaG93IHRoYXQgc2luY2UgdGhlIHR3byBvYmplY3RzIGhhdmUgc2ltaWxhciBjb2x1bW5zLCB0aGV5IHdvbnQgYmUgY29waWVkIG92ZXIuICANCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShwcnlyKQ0KDQojIE5vdGUgb2JqZWN0X3NpemUgbm90IG9iamVjdC5zaXplIA0KDQpkaWFtb25kcyA8LSBnZ3Bsb3QyOjpkaWFtb25kcw0KZGlhbW9uZHMyIDwtICBkaWFtb25kcyAlPiUNCiAgbXV0YXRlKHByaWNlcGVyY2FyYXQgPSBwcmljZSAvIGNhcmF0KQ0KDQpvYmplY3Rfc2l6ZShkaWFtb25kcykNCm9iamVjdF9zaXplKGRpYW1vbmRzMikNCm9iamVjdF9zaXplKGRpYW1vbmRzMiwgZGlhbW9uZHMpDQpgYGANCldoZW4geW91IG1vZGlmeSBvbmUgb2YgdGhlbSwgdGhlbiB0aGF0IHZhcmlhYmxlIGNhbiBubyBsb25nZXIgYmUgc2hhcmVkIGJldHdlZW4gdGhlIHR3byBkYXRhIGZyYW1lcyBhbmQgYSBjb3B5IG11c3QgYmUgbWFkZS4gVGhlIHNpemUgb2YgZWFjaCBkYXRhIGZyYW1lIGlzIHVuY2hhbmdlZCwgYnV0IHRoZSBjb2xsZWN0aSBzaXplIGluY3JlYXNlczoNCg0KYGBge3J9DQpkaWFtb25kcyRjYXJhdFsxXSA8LSAgTkENCm9iamVjdF9zaXplKGRpYW1vbmRzKQ0Kb2JqZWN0X3NpemUoZGlhbW9uZHMyKQ0Kb2JqZWN0X3NpemUoZGlhbW9uZHMyLCBkaWFtb25kcykNCg0KYGBgDQoNCg0KPGgzPk92ZXJ3cml0ZSB0aGUgb3JpZ2luYWwgPC9oMz4NCkluc3RlYWQgb2YgY3JlYXRpbmcgaW50ZXJtZWRpYXQgZW9iamVjdHMgYXQgZWFjaCBzdHBlLCB3ZSBjb3VsZCBvdmVyd3JpdGUgdGhlIG9yaWdpbmFsIG9iamVjdC4gVGhpcyBpcyBsZXNzIHR5cGluZyBhbmQgbGVzcyBtaXN0YWtlcywgaG93ZXZlciwgZGVidWdnaW5nIGlzIHBhaW5mdWwuIFRoZSByZXBldGl0aW9uIG9mIHRoZSBvYmplY3QgYmVpbmcgdHJhbnNmb3JtZWQgb2JzY3VyZXMgd2hhdCdzIGNoYWduZWluZyBvbiBlYWNoIGxpbmUuIA0KDQo8aDM+IEZ1bmN0aW9uIGNvbXBvc2l0aW9uIDwvaDM+DQpKdXN0IHN0cmluZyB0aGUgZnVuY3Rpb25zIGNhbGxzIHRvZ2V0aGVyLiBUaGUgZGlzYWR2YW50YWdlIGlzIHRoYXQgeW91IGhhdmUgdG8gcmVhZCBmcm9tIGluc2lkZSBvdXQuIEZyb20gcmlnaHQgdG8gbGVmdCBhbmQgdGhhdCB0aGUgYXJndW1lbnRzIGVuZCB1cCBzcHJlYWQgZmFyIGFwYXJ0LiBUaGUgZGFnd29vZCBzYW5kd2ljaCBwcm9ibGVtLiANCg0KPGgzPiBVc2UgdGhlIHBpcGUgPC9oMz4NClRoZSBwaXBlIHdvcmtzIGJ5IHBlcmZvcm1pbmcgYSBsZXhpY2FsIHRyYW5zZm9ybWF0aW9uIGJlaGluZCB0aGUgc2NlbmVzLiBNYWdyaXR0ciByZWFzc2VtYmVzIHRoZSBjb2RlIGluIHRoZSBwaXBlIHRvIGEgZm9ybSB0aGF0IHdvcmtzIGJ5IG92ZXJ3cml0aW5nIGFuIGludGVybWVkaWF0ZSBvYmplY3QuIFBpcGUgd29uJ3Qgd29yayBmb3IgdHdvIGNsYXNzZXMgb2YgZnVuY3Rpb25zOg0KPGgzPiBmdW5jdGlvbnMgdGhhdCB1c2UgdGhlIGN1cnJlbnQgZW52aXJvbm1lbnQ8L2gzPg0KRm9yIGV4YW1wbGUsIGFzc2lnbigpIHdpbGwgY3JlYXRlIGEgbmV3IHZhcmlhYmxlIHdpdGggdGhlIGdpdmVuIG5hbWUgaW4gdGhlIGN1cnJlbnQgZW5pdnJvbm1lbnQ6DQoNCmBgYHtyfQ0KYXNzaWduKCJ4IiwxMCkNCngNCiJ4IiAlPiUgYXNzaWduKDEwMCkNCngNCg0KYGBgDQpUaGUgdXNlIG9mIGFzc2lnbiB3aXRoIHRoZSBwaXBlIGRvZXMgbm90IHdvcmsgYmVjYXVzZSBpdCBhc3NpZ25zIGl0IHRvIGEgdGVtcG9yYXJ5IGVudmlyb25tZW50IHVzZWQgYnkgJT4lLiBJZiB5b3UgZG8gd2FudCB0byB1c2UgYXNzaWduIHdpdGggdGhlIHBpcGUsIHlvdSBtdXN0IGJlIGV4cGxpY2l0IGFib3V0IHRoZSBlbnZpcm9ubWVudDoNCg0KYGBge3J9DQplbnYgPC0gZW52aXJvbm1lbnQoKQ0KIngiICU+JSBhc3NpZ24oMTAwLCBlbnZpciA9IGVudikNCngNCg0KYGBgDQoNCk90aGVyIGZ1bmN0aW9ucyBpbmNsdWRlOiBnZXQoKSBhbmQgbG9hZCgpLiANCg0KPGgzPiBGdW5jdGlvbnMgdGhhdCB1c2UgbGF6eSBldmFsdWF0aW9uPC9oMz4NCkluIFIgZnVuY3Rpb24gYXJndW1lbnRzIGFyZSBvbmx5IGNvbXB1dGVkIHdoZW4gdGhlIGZ1bmN0aW9uIHVzZXMgdGhlbSwgbm90IHByaW9yIHRvIGNhbGxpbmcgdGhlIGZ1bmN0aW9uLiBUaGUgcGlwZSBjb21wdXRlcyBlYWNoIGVsZW1lbnQgaW4gdHVybiwgc28geW91IGNhbid0IHJlbHkgb24gdGhpcyBiZWhhdmlvci4gIE9uZSBwbGFjZSB0aGF0IHRoaXMgaXMgYSBwcm9ibGVtIGlzIHRyeUNhdGNoKCkgd2hpY2ggbGV0cyB5b3UgY2FwdHVyZSBhbmQgaGFuZGxlIGVycm9ycy4NCg0KYGBge3J9DQp0cnlDYXRjaChzdG9wKCIhIiksIGVycm9yID0gZnVuY3Rpb24oZSkgIkFuIGVycm9yIikNCnN0b3AoIiEiKSAlPiUNCiAgdHJ5Q2F0Y2goZXJyb3IgPSBmdW5jdGlvbihlKSAiQW4gZXJyb3IiKQ0KICAgICAgICAgICAgIA0KYGBgDQoNClRoZXJlIGFyZSByZWxhdGl2ZWx5IHdpZGUgY2xhc3Mgb2YgZnVuY3Rpb25zIHdpdGggdGhpcyBiZWhhdmlvciwgaW5jbHVkaW5nIHRyeSgpLCBzdXBwcmVzc01lc3NhZ2VzKCksIGFuZCBzdXBwcmVzc1dhcm5uZ3MoKSBpbiBiYXNlIFINCg0KPGgyPiBXaGVuIE5vdCB0byB1c2UgUGlwZTwvaDI+DQpZb3VyIHBpcGVzIGFyZSBsb25nZXIgdGhhbiAxMCBzdGVwcyA8L2JyPg0KWW91IGhhdmUgbXVsaXRwbGUgaW5wdXRzIG9yIG91dHB1dHMuIDwvYnI+DQpZb3UgYXJlIHRoaW5raW5nIGFib3V0IGEgZGlyZWN0ZWQgZ3JhcGggd2l0aCBhIGNvbXBsZXggZGVwZW5kZW5jeSBzdHJ1Y3R1cmUuIFBpcGVzIGFyZSBmdW5kYW1lbnRhbGx5IGxpbmVhciBhbmQgZXhwcmVzc2luZyBjb21wbGV4IHJlbGF0aW9uc2hpcHMgd2l0aCBwaXBlcyB3aWxsIHlpZWxkIGNvbmZ1c2luZyBjb2RlcyA8L3A+DQoNCjxoMj4gT3RoZXIgTWFncml0dHIgZnVuY3Rpb25zIDwvaDI+DQpUIHBpcGUuIFdvcmtzIGxpa2UgJT4lIGJ1dCByZXR1cm5zIHRoZSBsZWZ0IGhhbmQgc2lkZSByZXN1bHRzLg0KDQpgYGB7cn0NCnJub3JtKDEwMCkgJT4lDQogIG1hdHJpeChuY29sID0gMikgJT4lDQogIHBsb3QoKSU+JQ0KICBzdHIoKQ0KDQoNCmBgYA0KDQpgYGB7cn0NCnJub3JtKDEwMCkgJT4lDQogIG1hdHJpeChuY29sID0gMikgJVQ+JQ0KICBwbG90KCklPiUNCiAgc3RyKCkNCg0KYGBgDQpJZiB5b3UgYXJlIHdvcmtpbmcgd2l0aCBmdW5jdGlvbnMgdGhhdCBkb24ndCBoYXZlIGEgZGF0YSBmcmFtZSBiYXNlZCBBUEkgKGllLiB5b3UgcGFzcyB0aGVtIGluZGl2aWR1YWwgdmVjdG9ycywgbm90IGEgZGF0YSBmcmFtZSBhbmQgZXhwcmVzc2lvbnMgdG8gYmUgZXZhbHVhdGUgZGluIHRoZSBjb250ZXh0IG9mIHRoYXQgZGF0YSBmcmFtZSkgeW91IG1pZ2h0IGZpbmQgdGhlICUkJSB1c2VmdWwuIEl0IGV4cGxvZGVzIG91dCB0aGUgdmFyaWFibGVzIGluIGEgZGF0YSBmcmFtZSBzbyB0aGF0IHlvdSBjYW4gcmVmZXIgdG8gdGhlbSBleHBsaWNpdGx5LiBUaGlzIGlzIHVzZWZ1bCB3aGVuIHdvcmtpbmcgd2l0aCBtYW55IGZ1bmN0aW9ucyBpbiBiYXNlIFI6DQoNCmBgYHtyfQ0KbGlicmFyeShtYWdyaXR0cikNCm10Y2FycyAlJCUNCiAgY29yKGRpc3AsIG1wZykNCg0KYGBgDQoNCkZvciBhc3NpZ25tZW50IG1hZ3JpdHRyIHByb3ZpZGVzIHRoZSAlPD4lIG9wZXJhdG9yIHdoaWNoIGFsbG93cyB5b3UgdG8gcmVwbGFjZSBjb2RlIGxpa2UgdGhpczoNCg0KYGBge3J9DQptdGNhcnMgPC0gIG10Y2FycyAlPiUNCiAgdHJhbnNmb3JtKGN5bCA9IGN5bCAqMikNCm10Y2Fycw0KYGBgDQp3aXRoIHRoaXM6DQoNCmBgYHtyfQ0KbXRjYXJzICU8PiUgdHJhbnNmb3JtKGN5bCA9IGN5bCAqMikNCm10Y2Fycw0KYGBgDQoNCg==