Dplyr top_n returns multiple rows

Viewed 695

Dplyr provides a function top_n(), however in case of equal values it returns all rows (more than one). I would like to return exactly one row per group. See the example below.

df <- data.frame(id1=c(rep("A",3),rep("B",3),rep("C",3)),id2=c(8,8,4,7,7,4,5,5,5))
df %>% group_by(id1) %>% top_n(n=1)
3 Answers

You can use a combination of arrange and slice

df %>% 
  group_by(id1) %>% 
  arrange(desc(id2)) %>% 
  slice(1)

Use desc with in arrange if you want the larges element otherwise leave it out.

Apparently also slice_head is the new name of the function that you are looking for

df %>% 
  group_by(id1) %>% 
  arrange(desc(id2)) %>% 
  slice_head(id2, n=2)

Use slice_max() with the argument with_ties = FALSE:

library(dplyr)

df %>%
  group_by(id1) %>%
  slice_max(id2, with_ties = FALSE)

# A tibble: 3 x 2
# Groups:   id1 [3]
  id1     id2
  <chr> <dbl>
1 A         8
2 B         7
3 C         5

If you don't want to remember so many {dplyr} function names that are prone to be changed anyway, I can recommend the {data.table} package for such tasks. Plus, it's faster.

require(data.table)
df <- data.frame(id1=c(rep("A",3),rep("B",3),rep("C",3)),id2=c(8,8,4,7,7,4,5,5,5))
setDT(df)
df[ ,
    .(id2_head = head(id2, 1)),
    by = id1 ]
Related