R 按组获取最高值

R 按组获取最高值,r,data.table,dplyr,R,Data.table,Dplyr,下面是一个示例数据框: d <- data.frame( x = runif(90), grp = gl(3, 30) ) 使用dplyr,我希望它能起作用: d %>% arrange_(~ desc(x)) %>% group_by_(~ grp) %>% head(n = 5) 但它只返回前5行 将head交换为返回整个d d %>% arrange_(~ desc(x)) %>% group_by_(~ grp)

下面是一个示例数据框:

d <- data.frame(
  x   = runif(90),
  grp = gl(3, 30)
) 
使用
dplyr
,我希望它能起作用:

d %>%
  arrange_(~ desc(x)) %>%
  group_by_(~ grp) %>%
  head(n = 5)
但它只返回前5行

head
交换为返回整个
d

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

如何获得正确的子集?

在调用中,您需要将
包装起来。在以下代码中,
表示当前组(请参见
do
帮助页面中的
..
说明)


正如akrun所提到的,
slice
是一种替代方案

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

虽然我没有问这个问题,但为了完整性,一个可能的
data.table
版本是(感谢@Arun的修复):


我在base R中的方法是:

ordered <- d[order(d$x, decreasing = TRUE), ]
ordered[ave(d$x, d$grp, FUN = seq_along) <= 5L,]
dplyr基准
set.seed(123)
d%
分组依据(grp)%>%
顶部n(n=5,wt=x)},
dohead={d%>%
排列(~desc(x))%>%
分组依据(~grp)%>%
do(头(,n=5))},
切片={d%>%
排列(~desc(x))%>%
分组依据(~grp)%>%
切片(1:5)},
筛选器={d%>%
排列(描述(x))%>%
分组依据(grp)%>%

过滤(row_number()非常容易使用
数据。表也可以使用

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

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


添加稍快的
数据。表
解决方案:

set.seed(123L)
d <- data.frame(
    x   = runif(1e8),
    grp = sample(1e4, 1e8, TRUE))
setDT(d)
setorder(d, grp, -x)
dd <- copy(d)

library(microbenchmark)
microbenchmark(
    data.table3 = d[, indx := seq_len(.N), grp][indx <= 5L],
    data.table4 = dd[dd[, .I[seq_len(.N) <= 5L], grp]$V1],
    times = 10L
)
从“
slice\u min()
slice\u max()
选择具有变量最小值或最大值的行,从混乱的
top\n()中接管。


数据:
set.seed(123)
如果排序变量在每个组中不唯一,则top_n(n=1)仍将为每个组返回多行。为了精确地为每个组选择一个事件,请为每行添加一个唯一变量:

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)
set.seed(123)
d%
变异(rn=行数())%>%
分组依据(grp)%>%
顶部(n=1,wt=rn)

另一个
数据。表
解决方案强调其简明语法:

setDT(d)
d[order(-x), .SD[1:5], grp]

@akrun谢谢。我不知道这个函数。@Davidernburg谢谢。这就是匆忙发布答案的结果。我已经删除了这些废话。Richie,FWIW你只需要一个小的添加:
setDT(d)[order(-x),head(.SD,5L),by=grp]
这个答案有点过时了,但是第二部分是我自动的方法,如果你去掉
并使用
排列
分组
而不是
排列
分组
@akrun
过滤器
需要一个附加功能,而
切片
版本不知道你为什么要这么做不要在这里添加
data.table
)(我知道,我可以告诉你:因为问题是专门要求一个dplyr解决方案。我只是在开玩笑……这不像你从来没有(只是在相反的方向上)。@DavidArenburg,我不是说这是“非法的”或者任何类似于提供data.table答案的东西……当然,您可以这样做,并提供您喜欢的任何基准:)顺便说一句,您链接的问题是一个很好的示例,其中dplyr语法更方便(我知道,主观!)比data.table.还有什么可以忽略关系的吗?@MatíasGuzmánNaranjo,添加另一个
数据。table
方法应该稍微快一点:
dt@chinsoon12请便。我没有时间再次对这些解决方案进行基准测试。添加另一个
数据。table
方法更简单:
setDT(d)[order(-x),x[1:5],keyby=(grp)]
@TaoHu这与前两种解决方案非常相似。我不认为
会打败
@DavidArenburg是的,我同意你的看法,我认为最大的区别是
setorder
order
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
library(data.table)
setorder(setDT(d), -x)[, head(.SD, 5), keyby = grp]
setorder(setDT(d), grp, -x)[, head(.SD, 5), by = grp]
setorder(setDT(d), grp, -x)[, indx := seq_len(.N), by = grp][indx <= 5]
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
set.seed(123L)
d <- data.frame(
    x   = runif(1e8),
    grp = sample(1e4, 1e8, TRUE))
setDT(d)
setorder(d, grp, -x)
dd <- copy(d)

library(microbenchmark)
microbenchmark(
    data.table3 = d[, indx := seq_len(.N), grp][indx <= 5L],
    data.table4 = dd[dd[, .I[seq_len(.N) <= 5L], grp]$V1],
    times = 10L
)
Unit: milliseconds
        expr      min       lq     mean   median        uq      max neval
 data.table3 826.2148 865.6334 950.1380 902.1689 1006.1237 1260.129    10
 data.table4 729.3229 783.7000 859.2084 823.1635  966.8239 1014.397    10
d %>% group_by(grp) %>% slice_max(order_by = x, n = 5)
# # A tibble: 15 x 2
# # Groups:   grp [3]
#     x grp  
# <dbl> <fct>
#  1 0.994 1    
#  2 0.957 1    
#  3 0.955 1    
#  4 0.940 1    
#  5 0.900 1    
#  6 0.963 2    
#  7 0.902 2    
#  8 0.895 2    
#  9 0.858 2    
# 10 0.799 2    
# 11 0.985 3    
# 12 0.893 3    
# 13 0.886 3    
# 14 0.815 3    
# 15 0.812 3
d %>%
  group_by(grp) %>%
  top_n(n = 5, wt = x)
set.seed(123)
d <- data.frame(
  x = runif(90),
  grp = gl(3, 30))
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)
setDT(d)
d[order(-x), .SD[1:5], grp]