Tidyverse: фильтрация n крупнейших групп в сгруппированном фрейме данных - PullRequest
0 голосов
/ 27 сентября 2018

Я хочу отфильтровать n самых больших групп по количеству, а затем выполнить некоторые вычисления на отфильтрованном фрейме данных

Вот некоторые данные

Brand <- c("A","B","C","A","A","B","A","A","B","C")
Category <- c(1,2,1,1,2,1,2,1,2,1)
Clicks <- c(10,11,12,13,14,15,14,13,12,11)
df <- data.frame(Brand,Category,Clicks)

|Brand | Category| Clicks|
|:-----|--------:|------:|
|A     |        1|     10|
|B     |        2|     11|
|C     |        1|     12|
|A     |        1|     13|
|A     |        2|     14|
|B     |        1|     15|
|A     |        2|     14|
|A     |        1|     13|
|B     |        2|     12|
|C     |        1|     11|

Это мой ожидаемый результат.Я хочу отфильтровать два крупнейших бренда по количеству, а затем найти средние клики в каждой комбинации бренда / категории

|Brand | Category| mean_clicks|
|:-----|--------:|-----------:|
|A     |        1|        12.0|
|A     |        2|        14.0|
|B     |        1|        15.0|
|B     |        2|        11.5|

Что, я думал, может быть достигнуто с помощью кода, подобного этому (но не может)

df %>%
  group_by(Brand, Category) %>%
  top_n(2, Brand) %>% # Largest 2 brands by count
  summarise(mean_clicks = mean(Clicks))

РЕДАКТИРОВАТЬ: идеальный ответ должен быть в состоянии использоваться для таблиц базы данных, а также локальных таблиц

Ответы [ 6 ]

0 голосов
/ 08 октября 2018

Немного отличается от описанного выше.Просто потому, что я не люблю использовать соединение с большими наборами данных.Некоторым людям может не понравиться то, что я создаю и удаляю небольшой фрейм данных, извините: (

df %>% count(Brand) %>% top_n(2,n) -> Top2
df %>% group_by(Brand, Category) %>% 
filter(Brand %in% Top2$Brand) %>% 
summarise(mean_clicks = mean(Clicks))
remove(Top2)
0 голосов
/ 03 октября 2018

Другое решение dplyr, использующее join для фильтрации фрейма данных:

library(dplyr)

df %>%
  group_by(Brand) %>%
  summarise(n = n()) %>%
  top_n(2) %>% # select top 2
  left_join(df, by = "Brand") %>% # filters out top 2 Brands
  group_by(Brand, Category) %>%
  summarise(mean_clicks = mean(Clicks))

# # A tibble: 4 x 3
# # Groups:   Brand [?]
#   Brand Category mean_clicks
#   <fct>    <dbl>       <dbl>
# 1 A            1        12  
# 2 A            2        14  
# 3 B            1        15  
# 4 B            2        11.5
0 голосов
/ 03 октября 2018

Как насчет этого подхода, используя table, из базы R -

df %>%
  filter(Brand %in% names(tail(sort(table(Brand)), 2))) %>%
  group_by(Brand, Category) %>%
  summarise(mean_clicks = mean(Clicks))

# A tibble: 4 x 3
# Groups:   Brand [?]
  Brand Category mean_clicks
  <chr>    <dbl>       <dbl>
1 A         1.00        12.0
2 A         2.00        14.0
3 B         1.00        15.0
4 B         2.00        11.5
0 голосов
/ 27 сентября 2018

Другое решение dplyr:

df %>%
  group_by(Brand) %>%
  mutate(n = n()) %>%
  ungroup() %>%
  mutate(rank = dense_rank(desc(n))) %>%
  filter(rank == 1 | rank == 2) %>%
  group_by(Brand, Category) %>%
  summarise(mean_clicks = mean(Clicks))

# A tibble: 4 x 3
# Groups:   Brand [?]
  Brand Category mean_clicks
  <fct>    <dbl>       <dbl>
1 A           1.        12.0
2 A           2.        14.0
3 B           1.        15.0
4 B           2.        11.5

Или упрощенная версия (на основе предложений @camille):

df %>%
  group_by(Brand) %>%
  mutate(n = n()) %>%
  ungroup() %>%
  filter(dense_rank(desc(n)) < 3) %>%
  group_by(Brand, Category) %>%
  summarise(mean_clicks = mean(Clicks))
0 голосов
/ 27 сентября 2018

A Идея состоит в том, чтобы получить счетчики, сгруппированные по Brands и отфильтровать первые два (после упорядочения в порядке убывания).Затем мы объединяем исходный фрейм данных и находим среднее значение, сгруппированное по (Brand, Category)

library(data.table)

#Convert to data.table
dt1 <- setDT(df)

dt1[dt1[, .(cnt = .N), by = Brand][
             order(cnt, decreasing = TRUE), .SD[1:2]][,cnt := NULL], 
                   on = 'Brand'][, .(means = mean(Clicks)), by = .(Brand, Category)][]

, что дает

   Brand Category means
1:     A        1  12.0
2:     A        2  14.0
3:     B        2  11.5
4:     B        1  15.0
0 голосов
/ 27 сентября 2018

РЕДАКТИРОВАТЬ

Основываясь на обновленном вопросе, мы можем сначала добавить столбец подсчета, отфильтровать только верхние значения n групп, затем group_by Brand и Category внайдите mean для каждой группы.

df %>%
  add_count(Brand, sort = TRUE) %>%
  filter(n %in% head(unique(n), 2)) %>%
  group_by(Brand, Category) %>%
  summarise(mean_clicks = mean(Clicks))


#   Brand Category mean_clicks
#   <fct>    <dbl>       <dbl>
#1 A            1        12  
#2 A            2        14  
#3 B            1        15  
#4 B            2        11.5

Оригинальный ответ

Мы можем group_by Brand и выполнить все вычисления по группам, а затем отфильтроватьтоп группы по top_n

library(dplyr)
df %>%
  group_by(Brand) %>%
  summarise(n = n(), 
            mean = mean(Clicks)) %>%
  top_n(2, n) %>%
  select(-n)

#  Brand  mean
#  <fct> <dbl>
#1  A      12.8
#2  B      12.7
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...