Подсчет количества строк для разных комбинаций факторов - PullRequest
2 голосов
/ 31 января 2020

Рассматривая набор данных, такой как классический mtcars, я хочу знать количество наблюдений (= строк) по различным уровням факторов, принимая их как отдельно, так и вместе.

Например, следующий код сгенерирует столбец N с количеством наблюдений на уровень цил и передач, но не с количеством наблюдений цил и передач отдельно.

mtcars %>% dplyr::group_by(cyl, gear) %>% dplyr::summarise(N = n()) 

Я знаю, что это отдельный номер наблюдений для цил и передача может быть получена аналогичным образом, создавая отдельные кадры данных и объединяя все вместе. Следующее будет генерировать ожидаемый результат:

df <- mtcars %>% dplyr::group_by(cyl, gear) %>% dplyr::summarise(N = n())
df_gear <- mtcars %>% dplyr::group_by(gear) %>% dplyr::summarise(Ngear = n())
df_cyl <- mtcars %>% dplyr::group_by(cyl) %>% dplyr::summarise(Ncyl = n())
df %>% dplyr::left_join(df_cyl) %>% dplyr::left_join(df_gear)

Но мне интересно, есть ли более чистый способ генерации этого набора данных, надеюсь, без необходимости создавать промежуточные наборы данных.

Ответы [ 6 ]

5 голосов
/ 31 января 2020

Вот один из способов, которым вы могли бы подойти к этому, полагаясь на mutate() и ave() вместо group_by() и summarise() для компактности:

library(dplyr)

mtcars %>% 
  mutate(n = ave(cyl, cyl, gear, FUN = length),
         n_cyl = ave(cyl, cyl, FUN = length),
         n_gear = ave(gear, gear, FUN = length)) %>%
  select(gear, cyl, n, n_cyl, n_gear) %>%
  distinct()

  gear cyl  n n_cyl n_gear
1    4   6  4     7     12
2    4   4  8    11     12
3    3   6  2     7     15
4    3   8 12    14     15
5    3   4  1    11     15
6    5   4  2    11      5
7    5   8  2    14      5
8    5   6  1     7      5
3 голосов
/ 31 января 2020

Немного взлома, но без каких-либо промежуточных структур.

mtcars                             %>% 
mutate(cylgear = paste(cyl, gear)) %>% 
group_by(cylgear, cyl, gear)       %>%
summarise(combination = length(cylgear), Ngear = length(gear), Ncyl = length(cyl))
#> Joining, by = "cyl"
#> Joining, by = "gear"
#> # A tibble: 8 x 5
#> # Groups:   cyl [3]
#>     cyl  gear     N  Ncyl Ngear
#>   <dbl> <dbl> <int> <int> <int>
#> 1     4     3     1    11    15
#> 2     4     4     8    11    12
#> 3     4     5     2    11     5
#> 4     6     3     2     7    15
#> 5     6     4     4     7    12
#> 6     6     5     1     7     5
#> 7     8     3    12    14    15
#> 8     8     5     2    14     5
2 голосов
/ 31 января 2020

Вот способ использования комбинаций, затем l oop, получить счетчики и, наконец, слить рекурсивно:

# get all combinations of columns
x1 <- c("cyl", "gear")
x2 <- do.call(c, lapply(seq_along(x1), combn, x = x1, simplify = FALSE))

# group by all combos get count, then merge list of dataframes using reduce
res <- purrr::reduce(
  lapply(x2, function(i) mtcars %>% 
           group_by_at(i) %>% 
           mutate(N = n()) %>% 
           select_at(c(x1, "N")) %>% 
           unique()),
  left_join, by = x1)

# prettify the columns
myNames <- paste0("N_", sapply(x2, paste, collapse = "_"))
colnames(res)[ -c(1:(ncol(res) - length(myNames))) ] <- myNames

res
# # A tibble: 8 x 5
# # Groups:   cyl [3]
#     cyl  gear N_cyl N_gear N_cyl_gear
#   <dbl> <dbl> <int>  <int>      <int>
# 1     6     4     7     12          4
# 2     4     4    11     12          8
# 3     6     3     7     15          2
# 4     8     3    14     15         12
# 5     4     3    11     15          1
# 6     4     5    11      5          2
# 7     8     5    14      5          2
# 8     6     5     7      5          1
1 голос
/ 31 января 2020

Другой способ, который использует NSE и создает список данных, равный длине групп.

library(dplyr)
#Columns can be created programatically as well if needed all the combination
cols <- list('cyl', 'gear', c('cyl', 'gear'))


purrr::map(cols, ~count(mtcars, !!!syms(.x), 
                   name = paste0('n_', paste0(.x, collapse = ''))))

#[[1]]
# A tibble: 3 x 2
#    cyl n_cyl
#  <dbl> <int>
#1     4    11
#2     6     7
#3     8    14

#[[2]]
# A tibble: 3 x 2
#   gear n_gear
#  <dbl>  <int>
#1     3     15
#2     4     12
#3     5      5

#[[3]]
# A tibble: 8 x 3
#    cyl  gear n_cylgear
#  <dbl> <dbl>     <int>
#1     4     3         1
#2     4     4         8
#3     4     5         2
#4     6     3         2
#5     6     4         4
#6     6     5         1
#7     8     3        12
#8     8     5         2
1 голос
/ 31 января 2020

Строго говоря, tidyverse подход, но вы также можете сделать:

mtcars %>%
 mutate(Ncyl = with(stack(table(cyl)), values[match(cyl, ind)]),
        Ngear = with(stack(table(gear)), values[match(gear, ind)])) %>%
 group_by(cyl, gear) %>%
 summarise(N = n(),
           Ncyl = first(Ncyl),
           Ngear = first(Ngear))

    cyl  gear     N  Ncyl Ngear
  <dbl> <dbl> <int> <int> <int>
1     4     3     1    11    15
2     4     4     8    11    12
3     4     5     2    11     5
4     6     3     2     7    15
5     6     4     4     7    12
6     6     5     1     7     5
7     8     3    12    14    15
8     8     5     2    14     5
0 голосов
/ 31 января 2020

с мутатом

mtcars %>%
  group_by(cyl, gear) %>%
  mutate(N = n()) %>%
  group_by(gear) %>%
  mutate(Ngear = n()) %>%
  group_by(cyl) %>%
  mutate(Ncyl = n()) %>%
  select(cyl, gear, N, Ngear, Ncyl) %>%
  distinct()

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...