按组获得最高值

这是一个示例数据框架:

d <- data.frame( x = runif(90), grp = gl(3, 30) ) 

我想要d的子集,包含每个值为grp的前5个值为x的行。

使用base-R,我的方法是这样的:

 ordered <- d[order(d$x, decreasing = TRUE), ] splits <- split(ordered, ordered$grp) heads <- lapply(splits, head) do.call(rbind, heads) ## x grp ## 1.19 0.8879631 1 ## 1.4 0.8844818 1 ## 1.12 0.8596197 1 ## 1.26 0.8481809 1 ## 1.18 0.8461516 1 ## 1.29 0.8317092 1 ## 2.31 0.9751049 2 ## 2.34 0.9269764 2 ## 2.57 0.8964114 2 ## 2.58 0.8896466 2 ## 2.45 0.8888834 2 ## 2.35 0.8706823 2 ## 3.74 0.9884852 3 ## 3.73 0.9837653 3 ## 3.83 0.9375398 3 ## 3.64 0.9229036 3 ## 3.69 0.8021373 3 ## 3.86 0.7418946 3 

使用dplyr ,我期望这个工作:

 d %>% arrange_(~ desc(x)) %>% group_by_(~ grp) %>% head(n = 5) 

但它只返回整个前5行。

交换top_n返回整个d

 d %>% arrange_(~ desc(x)) %>% group_by_(~ grp) %>% top_n(n = 5) 

我如何得到正确的子集?

from ?top_n ,“用于sorting的variables默认为 tbl中的最后一个variables ”。 数据集中的最后一个variables是“grp”,它不是您想要排名的variables,这就是您的top_n尝试“返回整个d”的原因。 因此,如果您希望在数据集中按“x”sorting,您需要指定wt = x

 set.seed(123) d <- data.frame( x = runif(90), grp = gl(3, 30)) d %>% group_by(grp) %>% top_n(n = 5, wt = x) # x grp # 1 0.9404673 1 # 2 0.9568333 1 # 3 0.8998250 1 # 4 0.9545036 1 # 5 0.9942698 1 # 6 0.9630242 2 # 7 0.9022990 2 # 8 0.8578277 2 # 9 0.7989248 2 # 10 0.8950454 2 # 11 0.8146400 3 # 12 0.8123895 3 # 13 0.9849570 3 # 14 0.8930511 3 # 15 0.8864691 3 

data.table也很容易

 library(data.table) setorder(setDT(d), -x)[, head(.SD, 5), keyby = grp] 

要么

 setorder(setDT(d), grp, -x)[, head(.SD, 5), by = grp] 

或者(对于大数据集应该更快,因为避免为每个组调用.SD

 setorder(setDT(d), grp, -x)[, indx := seq_len(.N), by = grp][indx <= 5] 

编辑:这是如何dplyr比较data.table (如果任何人有兴趣)

 set.seed(123) d <- data.frame( x = runif(1e6), grp = sample(1e4, 1e6, TRUE)) library(dplyr) library(microbenchmark) library(data.table) dd <- copy(d) microbenchmark( top_n = {d %>% group_by(grp) %>% top_n(n = 5, wt = x)}, dohead = {d %>% arrange_(~ desc(x)) %>% group_by_(~ grp) %>% do(head(., n = 5))}, slice = {d %>% arrange_(~ desc(x)) %>% group_by_(~ grp) %>% slice(1:5)}, filter = {d %>% arrange(desc(x)) %>% group_by(grp) %>% filter(row_number() <= 5L)}, data.table1 = setorder(setDT(dd), -x)[, head(.SD, 5L), keyby = grp], data.table2 = setorder(setDT(dd), grp, -x)[, head(.SD, 5L), grp], data.table3 = setorder(setDT(dd), grp, -x)[, indx := seq_len(.N), grp][indx <= 5L], times = 10, unit = "relative" ) # expr min lq mean median uq max neval # top_n 24.246401 24.492972 16.300391 24.441351 11.749050 7.644748 10 # dohead 122.891381 120.329722 77.763843 115.621635 54.996588 34.114738 10 # slice 27.365711 26.839443 17.714303 26.433924 12.628934 7.899619 10 # filter 27.755171 27.225461 17.936295 26.363739 12.935709 7.969806 10 # data.table1 13.753046 16.631143 10.775278 16.330942 8.359951 5.077140 10 # data.table2 12.047111 11.944557 7.862302 11.653385 5.509432 3.642733 10 # data.table3 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 10 

你需要打个电话才能do 。 在下面的代码中. 表示当前组(请参阅do帮助页面中的说明)。

 d %>% arrange_(~ desc(x)) %>% group_by_(~ grp) %>% do(head(., n = 5)) 

正如akrun所提到的, slice是另一种select。

 d %>% arrange_(~ desc(x)) %>% group_by_(~ grp) %>% slice(1:5) 

我在基地R的方法是:

 ordered <- d[order(d$x, decreasing = TRUE), ] ordered[ave(d$x, d$grp, FUN = seq_along) <= 5L,] 

而使用dplyr, slice的方法可能是最快的,但你也可以使用filter ,比使用do(head(., 5))更快:

 d %>% arrange(desc(x)) %>% group_by(grp) %>% filter(row_number() <= 5L) 

dplyr基准

 set.seed(123) d <- data.frame( x = runif(1e6), grp = sample(1e4, 1e6, TRUE)) library(microbenchmark) microbenchmark( top_n = {d %>% group_by(grp) %>% top_n(n = 5, wt = x)}, dohead = {d %>% arrange_(~ desc(x)) %>% group_by_(~ grp) %>% do(head(., n = 5))}, slice = {d %>% arrange_(~ desc(x)) %>% group_by_(~ grp) %>% slice(1:5)}, filter = {d %>% arrange(desc(x)) %>% group_by(grp) %>% filter(row_number() <= 5L)}, times = 10, unit = "relative" ) Unit: relative expr min lq median uq max neval top_n 1.042735 1.075366 1.082113 1.085072 1.000846 10 dohead 18.663825 19.342854 19.511495 19.840377 17.433518 10 slice 1.000000 1.000000 1.000000 1.000000 1.000000 10 filter 1.048556 1.044113 1.042184 1.180474 1.053378 10 

如果sortingvariables在每个组中都不唯一,则top_n(n = 1)仍然会为每个组返回多行。 为了精确地为每个组select一个事件,请为每行添加一个唯一variables:

 set.seed(123) d <- data.frame( x = runif(90), grp = gl(3, 30)) d %>% mutate(rn = row_number()) %>% group_by(grp) %>% top_n(n = 1, wt = rn)