R dplyr group_by также учитывает пустые группы - PullRequest
1 голос
/ 19 марта 2019

Рассмотрим следующий фрейм данных:

set.seed(123)
data <- data.frame(col1 = factor(rep(c("A", "B", "C"), 4)),
                   col2 = factor(c(rep(c("A", "B", "C"), 3), c("A", "A", "A"))),
                   val1 = 1:12,
                   val2 = rnorm(12, 10, 15))

Таблица непредвиденных расходов выглядит следующим образом:

cont_tab <- table(data$col1, data$col2, dnn = c("col1", "col2"))

cont_tab

    col2
col1 A B C
   A 4 0 0
   B 1 3 0
   C 1 0 3

Как видите, некоторые пары не встречались: (A, B), (A, C), (B, C), (C, B). Конечная цель моего анализа - перечислить все пары (в данном случае 9) и показать статистику для каждой из них. При использовании функции dplyr::group_by() я столкнулся с ограничением. А именно, dplyr::group_by() рассматривает только существующие пары (пары, которые встречались хотя бы один раз):

data %>%
  group_by(col1, col2) %>%
  summarize(stat = sum(val2) - sum(val1))

# A tibble: 5 x 3
# Groups:   col1 [?]
  col1  col2   stat
  <fct> <fct> <dbl>
1 A     A      58.1
2 B     A     -16.4
3 B     B      17.0
4 C     A     -12.9
5 C     C     -41.9

Вывод, который я имею в виду, состоит из 9 строк (4 из которых имеют stat, равный 0). Это выполнимо в dplyr?

РЕДАКТИРОВАТЬ: Извините за слишком расплывчатые в начале. Реальная проблема более сложна, чем подсчет количества встреч конкретной пары. Я добавил новые данные, чтобы реальная проблема стала более заметной.

Ответы [ 5 ]

4 голосов
/ 19 марта 2019

Гораздо проще добавить spread из tidyr, чтобы получить тот же результат, что и с table

library(dplyr)
library(tidyr)
count(data, col1, col2) %>% 
      spread(col2, n, fill = 0)
# A tibble: 3 x 4
# Groups:   col1 [3]
#  col1      A     B     C
#  <fct> <dbl> <dbl> <dbl>
#1 A         4     0     0
#2 B         1     3     0
#3 C         1     0     3

ПРИМЕЧАНИЕ: шаг group_by/summarise изменен на count здесь

Как и предположил @divibisan, если OP требуется длинный формат, добавьте gather в конце

data %>%
   group_by(col1, col2) %>%
   summarize(stat = n()) %>%
   spread(col2, stat, fill = 0) %>%
   gather(col2, stat, A:C)
# A tibble: 9 x 3
# Groups:   col1 [3]
#  col1  col2   stat
#  <fct> <chr> <dbl>
#1 A     A         4
#2 B     A         1
#3 C     A         1
#4 A     B         0
#5 B     B         3
#6 C     B         0
#7 A     C         0
#8 B     C         0
#9 C     C         3

Обновление

С обновленными данными в посте ОП

data %>%
   group_by(col1, col2) %>%
   summarize(stat = sum(val2) - sum(val1)) %>% 
   spread(col2, stat, fill = 0)  %>% 
   gather(col2, stat, -1)
# A tibble: 9 x 3
# Groups:   col1 [3]
#  col1  col2    stat
#  <fct> <chr>  <dbl>
#1 A     A       7.76
#2 B     A     -20.8 
#3 C     A       6.97
#4 A     B       0   
#5 B     B      28.8 
#6 C     B       0   
#7 A     C       0   
#8 B     C       0   
#9 C     C       9.56
3 голосов
/ 19 марта 2019

Это выполнимо даже без dplyr

as.data.frame(table(data$col1, data$col2, dnn = c("col1", "col2")))
#  col1 col2 Freq
#1    A    A    4
#2    B    A    1
#3    C    A    1
#4    A    B    0
#5    B    B    3
#6    C    B    0
#7    A    C    0
#8    B    C    0
#9    C    C    3
2 голосов
/ 19 марта 2019

Вы можете использовать tidyr::complete

library(tidyverse)

data %>%
  group_by(col1, col2) %>%
  summarize(stat = n()) %>% 
  # additions below
  ungroup %>% 
  complete(col1, col2, fill = list(stat = 0))

# # A tibble: 9 x 3
#   col1  col2   stat
#   <chr> <chr> <dbl>
# 1 A     A         4
# 2 A     B         0
# 3 A     C         0
# 4 B     A         1
# 5 B     B         3
# 6 B     C         0
# 7 C     A         1
# 8 C     B         0
# 9 C     C         3

Вы также можете использовать count для первой части.Код ниже дает тот же вывод, что и код выше

data %>%
  count(col1, col2) %>%
  complete(col1, col2, fill = list(n = 0)) 
1 голос
/ 19 марта 2019

Также возможна tidyverse возможность использования tidyr::complete():

data %>% 
 group_by_all() %>%
 add_count() %>%
 complete(col1, col2, fill = list(n = 0)) %>%
 distinct()

  col1  col2      n
  <fct> <fct> <dbl>
1 A     A         4
2 A     B         0
3 A     C         0
4 B     A         1
5 B     B         3
6 B     C         0
7 C     A         1
8 C     B         0
9 C     C         3

Или использование tidyr::expand():

data %>% 
 count(col1, col2) %>%
 right_join(data %>%
            expand(col1, col2), by = c("col1" = "col1",
                                       "col2" = "col2")) %>%
 replace_na(list(n = 0))

Или использование tidyr::crossing():

data %>%
 count(col1, col2) %>%
 right_join(crossing(col1 = unique(data$col1), 
                     col2 = unique(data$col2)), by = c("col1" = "col1",
                                                       "col2" = "col2")) %>%
 replace_na(list(n = 0))
0 голосов
/ 19 марта 2019

Вот небольшой обходной путь, я надеюсь, что он работает для вас.Объедините ваш стол с таблицей всех комбинаций и замените NA на 0.

data %>%
group_by(col1, col2) %>%
summarize(stat = n()) %>% 
merge(unique(expand.grid(data)), by=c("col1","col2"), all=T) %>% 
replace_na(list(stat=0))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...