14

Can someone explain why table()doesn't work inside a chain of dplyr-magrittr piped operations? Here's a simple reprex:

tibble(
  type = c("Fast", "Slow", "Fast", "Fast", "Slow"),
  colour = c("Blue", "Blue", "Red", "Red", "Red")
) %>% table(.$type, .$colour)

Error in sort.list(y) : 'x' must be atomic for 'sort.list' Have you called 'sort' on a list?

But this works of course:

df <- tibble(
  type = c("Fast", "Slow", "Fast", "Fast", "Slow"),
  colour = c("Blue", "Blue", "Red", "Red", "Red")
) 

table(df$type, df$colour)


       Blue Red
  Fast    1   2
  Slow    1   1
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
RobertMyles
  • 2,673
  • 3
  • 30
  • 45

4 Answers4

19

This behavior is by design: https://github.com/tidyverse/magrittr/blob/00a1fe3305a4914d7c9714fba78fd5f03f70f51e/README.md#re-using-the-placeholder-for-attributes

Since you don't have a . on it's own, the tibble is still being passed as the first parameter so it's really more like

... %>% table(., .$type, .$colour)

The official magrittr work-around is to use curly braces

... %>% {table(.$type, .$colour)}
MrFlick
  • 195,160
  • 17
  • 277
  • 295
  • Ah! Same as another problem I had with the pipe, so (https://stackoverflow.com/questions/44007998/subset-filter-in-dplyr-chain-with-ggplot2). Looks like I'm a slow learner. Thanks! – RobertMyles Jun 13 '17 at 17:48
  • I thought using `do` would also do the trick... I guess it's because the output of `table` is not a data frame so something like `df %>% do(as.data.frame(table(.$type, .$colour)))` which looks horrible – Sotos Jun 13 '17 at 17:51
12

The %>% operator in dplyr is actually imported from magrittr. With magrittr, we can also use the %$% operator, which exposes the names from the previous expression:

library(tidyverse)
library(magrittr)

tibble(
  type = c("Fast", "Slow", "Fast", "Fast", "Slow"),
  colour = c("Blue", "Blue", "Red", "Red", "Red")
) %$% table(type, colour)

Output:

      colour
type   Blue Red
  Fast    1   2
  Slow    1   1
acylam
  • 18,231
  • 5
  • 36
  • 45
5

I've taken to using with(table(...)) like this:

tibble(type = c("Fast", "Slow", "Fast", "Fast", "Slow"),
       colour = c("Blue", "Blue", "Red", "Red", "Red")) %>% 
  with(table(type, colour))

And similar to the way we might read %>% as "and then" I would read that as "and then with that data make this table".

Brian Stamper
  • 2,143
  • 1
  • 18
  • 41
1

The tabyl function from Janitor package work with pipe well, a great substitute for the native table function.

library(janitor)
#> Warning: package 'janitor' was built under R version 4.1.3
#> 
#> Attaching package: 'janitor'
#> The following objects are masked from 'package:stats':
#> 
#>     chisq.test, fisher.test
mtcars %>%   tabyl(cyl, gear)
#>  cyl  3 4 5
#>    4  1 8 2
#>    6  2 4 1
#>    8 12 0 2

Created on 2023-05-03 with reprex v2.0.2

SJ9
  • 477
  • 4
  • 13