Search code examples
rtidyversemagrittr

Why does the colnames function generate a tibble (and not a vector), when used with the pipe operator and [ selector?


Question: Why does the colnames function generate a tibble, when used with the pipe operator %>% and the [ selector?

Example: Given the following tibble:

library(tidyverse)

x <- tribble(
  ~a, ~b,
  1, 0,
  0, 1
)

Problem: The colnames function generates a tibbe, when used with the pipe operator and the [ selector:

x %>% colnames(.)[1]
#> # A tibble: 2 x 1
#>       a
#>   <dbl>
#> 1    NA
#> 2    NA

Whereas I would expect it to generate a vector, as if no pipe operator was used:

colnames(x)[1]
#> [1] "a"

Solution

  • Because %>% auto-inserts . as the first parameter of the right-hand side, unless . is already used as a top-level parameter of the right-hand side.

    Your right-hand side is colnames(.)[1] — and . is not a top-level parameter here. In fact, the expression colnames(.)[1] is exactly equivalent to

    `[`(colnames(.), 1)
    

    So the top-level parameters of this expression are colnames(.) and 1. Neither is .. So %>% transforms your expression to

    `[`(x, colnames(x), 1)
    

    Which is once again equivalent to

    x[colnames(x), 1]
    

    And that expression doesn’t really make sense, and happens to result in what you’ve observed.

    To avoid . auto-insertion, you can wrap the expression into {…} as shown by akrun. Or you can split up the compound expressions into its constituent parts:

    x %>% colnames() %>% `[`(1L)