4 Pipes

4.1 Introduction

Use %>% to emphasise a sequence of actions, rather than the object that the actions are being performed on.

Avoid using the pipe when:

  • You need to manipulate more than one object at a time. Reserve pipes for a sequence of steps applied to one primary object.

  • There are meaningful intermediate objects that could be given informative names.

4.2 Whitespace

%>% should always have a space before it, and should usually be followed by a new line. After the first step, each line should be indented by two spaces. This structure makes it easier to add new steps (or rearrange existing steps) and harder to overlook a step.

# Good
iris %>%
  group_by(Species) %>%
  summarize_if(is.numeric, mean) %>%
  ungroup() %>%
  gather(measure, value, -Species) %>%
  arrange(value)

# Bad
iris %>% group_by(Species) %>% summarize_all(mean) %>%
ungroup %>% gather(measure, value, -Species) %>%
arrange(value)

4.3 Long lines

If the arguments to a function don’t all fit on one line, put each argument on its own line and indent:

iris %>%
  group_by(Species) %>%
  summarise(
    Sepal.Length = mean(Sepal.Length),
    Sepal.Width = mean(Sepal.Width),
    Species = n_distinct(Species)
  )

4.4 Short pipes

A one-step pipe can stay on one line, but unless you plan to expand it later on, you should consider rewriting it to a regular function call.

# Good
iris %>% arrange(Species)

iris %>% 
  arrange(Species)

arrange(iris, Species)

Sometimes it’s useful to include a short pipe as an argument to a function in a longer pipe. Carefully consider whether the code is more readable with a short inline pipe (which doesn’t require a lookup elsewhere) or if it’s better to move the code outside the pipe and give it an evocative name.

# Good
x %>%
  select(a, b, w) %>%
  left_join(y %>% select(a, b, v), by = c("a", "b"))

# Better
x_join <- x %>% 
  select(a, b, w)
y_join <- y %>%
  filter(!u) %>%
  gather(a, v, -b) %>%
  select(a, b, v)
left_join(x_join, y_join, by = c("a", "b"))

4.5 No arguments

magrittr allows you to omit () on functions that don’t have arguments. Avoid this feature.

# Good
x %>% 
  unique() %>%
  sort()

# Bad
x %>% 
  unique %>%
  sort

4.6 Assignment

There are three acceptable forms of assignment:

  • Variable name and assignment on separate lines:

    iris_long <-
      iris %>%
      gather(measure, value, -Species) %>%
      arrange(-value)
  • Variable name and assignment on the same line:

    iris_long <- iris %>%
      gather(measure, value, -Species) %>%
      arrange(-value)
  • Assignment at the end of the pipe with ->:

    iris %>%
      gather(measure, value, -Species) %>%
      arrange(-value) ->
      iris_long

    I think this is the most natural to write, but makes reading a little harder: when the name comes first, it can act as a heading to remind you of the purpose of the pipe.

The magrittr package provides the %<>% operator as for shortcut modifying an object in place. Avoid this operator.

# Good
x <- x %>% 
  abs() %>% 
  sort()
  
# Bad
x %<>%
  abs() %>% 
  sort()