使用 dplyr 的每个组的前 "n" 行——每个组的数量不同

Top "n" rows of each group using dplyr -- with different number per group

我将使用内置的 chickwts 数据作为示例。

这是数据,有 5 种 Feed 类型。

> head(chickwts)

  weight      feed
1    179 horsebean
2    160 horsebean
3    136 horsebean
4    227 horsebean
5    217 horsebean
6    168 horsebean

> table(chickwts$feed)

   casein horsebean   linseed  meatmeal   soybean sunflower 
       12        10        12        11        14        12 

我想要的是每种 Feed 类型按重量排列的前几行。但是,我需要为每种 Feed 类型分配不同的编号吗?例如,

top_n_feed <-
  c(
    "casein" = 3,
    "horsebean" = 5,
    "linseed" = 3,
    "meatmeal" = 6,
    "soybean" = 3,
    "sunflower" = 2
  )

如何使用 dplyr 执行此操作?

要按重量获取每种 Feed 类型的前 n 行,我可以使用如下代码,但我不确定如何将其扩展到每种 Feed 类型的不同数量。

chickwts %>%
  group_by(feed) %>% 
  slice_max(order_by = weight, n = 5)

这并不是 dplyr 说起来容易的事情。我建议合并数据然后过滤。


tibble(feed=names(top_n_feed), topn=top_n_feed) %>% 
  inner_join(chickwts) %>% 
  group_by(feed) %>% 
  arrange(desc(weight), .by_group=TRUE) %>% 
  filter(row_number() <= topn) %>%
  select(-topn)

另一种使用splitmap2的方法:

library(dplyr)
library(purrr)

chickwts %>%
filter(feed %in% names(top_n_feed)) %>%
split(.$feed) %>% 
map2_dfr(top_n_feed[names(.)], ~slice_max(.x, order_by = weight, n = .y))

top_n_feed 放入 chickwts 数据框和 select 每个组的顶部 n 行。

library(dplyr)

tibble::enframe(top_n_feed, name = 'feed') %>% 
        left_join(chickwts, by = 'feed') %>%
        group_by(feed) %>%
        top_n(first(value), weight)

#   feed      value weight
#   <chr>     <dbl>  <dbl>
# 1 casein        3    390
# 2 casein        3    379
# 3 casein        3    404
# 4 horsebean     5    179
# 5 horsebean     5    160
# 6 horsebean     5    227
# 7 horsebean     5    217
# 8 horsebean     5    168
# 9 linseed       3    309
#10 linseed       3    260
# … with 12 more rows

出于某种原因,我无法使 slice_sample 适用于此示例。

每当你有一个命名列表时,想想 purrr::imap。如果不需要,请避免连接,尤其是在大规模工作时。

library(dplyr)
library(purrr)

top_n_feed <- c(
    "casein" = 3,
    "horsebean" = 5,
    "linseed" = 3,
    "meatmeal" = 6,
    "soybean" = 3,
    "sunflower" = 2
  )

imap_dfr(top_n_feed, ~ filter(chickwts, feed %in% .y) %>% 
           slice_max(order_by = weight, n = .x))

   weight      feed
1     404    casein
2     390    casein
3     379    casein
4     227 horsebean
5     217 horsebean
6     179 horsebean
7     168 horsebean
8     160 horsebean
9     309   linseed
10    271   linseed
11    260   linseed
12    380  meatmeal
13    344  meatmeal
14    325  meatmeal
15    315  meatmeal
16    303  meatmeal
17    263  meatmeal
18    329   soybean
19    327   soybean
20    316   soybean
21    423 sunflower
22    392 sunflower