Верхние «n» строк каждой группы с использованием dplyr — с разным номером для каждой группы

#r #dplyr #data-cleaning #data-wrangling

#r #dplyr #очистка данных #перебор данных

Вопрос:

Я буду использовать встроенные chickwts данные в качестве примера.

Вот данные, существует 5 типов каналов.

 > 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 
 

Мне нужны верхние строки по весу для каждого типа подачи. Однако мне нужно другое число для каждого типа подачи? Например,

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

Как я могу это сделать с помощью dplyr ?

Чтобы получить верхние n строки каждого типа подачи по весу, я могу использовать код, как показано ниже, но я не уверен, как расширить это до другого числа для каждого типа подачи.

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

Ответ №1:

На самом деле это не то, что 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)

 

Комментарии:

1. Действительно, пропустил часть о порядке по весам. Это точно покроет его.

Ответ №2:

Каждый раз, когда у вас есть именованный список, подумайте 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
 

Ответ №3:

Другой способ с использованием split и map2 :

 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))
 

Комментарии:

1. Классный подход — не могли бы вы объяснить синтаксис .$feed в split ?

2. Это синтаксис для подмножества столбцов data.frame. . представляет data.frame из файла %>% (результат последнего шага).

Ответ №4:

Введите top_n_feed chickwts dataframe и выберите верхние 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 работу для этого примера.