按组获得最高值
这是一个示例数据框架:
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)