Extracting Columns from a Tibble

February 18, 2020    tibble

This is both a follow up post to my earlier post tibbles, data frames and vectors and a query that came up at work.

The central question was how can you extract a column from a tibble as part of a pipe. That might sound straight forward but let’s take a look.

# load packages and create a tibble to work with
library(tibble)
library(dplyr)
test = tibble(id = c("a", "b", "a"), val = c(1, 2, 3))
test
# A tibble: 3 x 2
  id      val
  <chr> <dbl>
1 a         1
2 b         2
3 a         3

Let’s run a quick filter() and then select() the values.

test %>%
  filter(id == "a") %>%
  select(val)
# A tibble: 2 x 1
    val
  <dbl>
1     1
2     3

But as we saw in my previous post, this is still a tibble, so if we pass it to something expecting a vector it doesn’t work.

test %>%
  filter(id == "a") %>%
  select(val) %>%
  mean()
Warning in mean.default(.): argument is not numeric or logical: returning NA
[1] NA

The Best Solution

The best solution to this problem is to use the pull() function. I missed this function when it first came out in dplyr 0.7.0 but now use it a lot.

# pull() gives us a vector 
test %>%
  filter(id == "a") %>%
  pull(val) 
[1] 1 3
# now we don't get an error
test %>%
  filter(id == "a") %>%
  pull(val) %>%
  mean()
[1] 2

Other Solutions

As with many, many things in R there are alternatives and for the moment we are just going to continue to work with a pipe. Some neat alternatives come from a special type of function called an infix function. You use these all the time, + and %>% being examples. These infix functions sit between arguments x + y but they can also be used in a more traditional f(x,y) manner. To use them in this way you need to use backticks so 2^3 = 8 becomes `^`(2,3) = 8 :

# Power
2 ^ 3
[1] 8
`^`(2,3)
[1] 8

This means that we can use [[]] and $ like this too. Take our tibble.

test$val
[1] 1 2 3
`$`(test, val)
[1] 1 2 3

or

test[[2]]
[1] 1 2 3
`[[`(test, 2)
[1] 1 2 3

This means we can add an infix function to the end of our pipe.

test %>%
  filter(id == "a") %>%
  `[[`(2)
[1] 1 3
test %>%
  filter(id == "a") %>%
  `$`(val)
[1] 1 3

Finally, we could just use getElement().

test %>%
  filter(id == "a") %>%
  getElement("val")
[1] 1 3