Создание всех комбинаций выборки из двух групп столбцов в R - PullRequest
1 голос
/ 05 августа 2020

У меня есть фрейм данных ниже и внутри этих «двух групп», столбцы A&B и D&E. Я хотел бы найти все комбинации, а затем сгруппировать по всем комбинациям применения различных фильтров в столбцах A&B и D&E, но в форме выбора только 1 столбца из каждой группы одновременно. Я не знаю правильной формулы для этого, и на самом деле проблема намного больше.

df =

     Size    A     B     D     E
       1     1     1     0     0
       5     0     0     1     0
       10    1     1     1     0
       3     1     0     0     0
       2     1     1     1     1
       55    0     0     0     1
       5     1     0     1     1
       2     0     0     1     1
       1     1     1     1     1
       4     1     1     1     0

Таким образом, комбинации для фильтрации должны быть

Filter 1 : A = 1 AND D = 1

Фильтр 2: A = 1 AND D = 0

Фильтр 3: A = 1 AND E = 1

Фильтр 4: A = 1 И E = 0

Фильтр 5: A = 0 И D = 1

Фильтр 6: A = 0 И D = 0

Фильтр 7: A = 0 И E = 1

Фильтр 8: A = 0 И E = 0

Фильтр 9: B = 1 И D = 1

Фильтр 10: B = 1 И D = 0

Фильтр 11: B = 1 AND E = 1

Фильтр 12: B = 1 AND E = 0

Фильтр 13: B = 0 AND D = 1

Фильтр 14: B = 0 AND D = 0

Фильтр 15: B = 0 AND E = 1

Фильтр 16: B = 0 AND E = 0

Я хочу найти способ эффективно создать эти группы фильтров (всегда используя 1 фильтр из столбцов A&B или D&E), а затем найти среднее значение и количество столбцов размера для каждой настройки фильтра. Мне удалось сделать это только без разных групп для отбора проб фильтра.

То, что я пробовал, было в форме:

groupNames <- names(df)[2:5]

myGroups <- Map(combn,list(groupNames),seq_along(groupNames),simplify = FALSE) %>% unlist(recursive = FALSE)

results = lapply(myGroups, FUN = function(x) {do.call(what = group_by_, args = c(list(df), x)) %>% summarise( n = length(Size), avgVar1 = mean(Size))})

Он обрабатывает четыре столбца одинаково и не учитывает выборку из 2 групп. Что я мог сделать с кодом, чтобы все заработало?

Большое спасибо.

Ответы [ 2 ]

1 голос
/ 05 августа 2020
library(tidyverse)
df <- tribble(~Size, ~A, ~B, ~D, ~E,
              1, "1", "1", "0", "0",
              5, "0", "0", "1", "0",
              10, "1", "1", "1", "0",
              3, "1", "0", "0", "0",
              2, "1", "1", "1", "1",
              55, "0", "0", "0", "1",
              5, "1", "0", "1", "1",
              2, "0", "0", "1", "1",
              1, "1", "1", "1", "1",
              4, "1", "1", "1", "0")
p <- function(...) paste0(...) # for legibility, should rather use glue

all_filtering_groups <- list(c("A", "B"), c("D", "E")) # assuming these are known
all_combns <- map(1:length(all_filtering_groups), ~ combn(all_filtering_groups, .))
res <- list(length(all_combns))

#microbenchmark::microbenchmark({
for(comb_length in seq_along(all_combns)){
  res[[comb_length]] <- list(ncol(all_combns[[comb_length]]))
  for(col_i in seq_len(ncol(all_combns[[comb_length]]))){
    
    filtering_groups <- all_combns[[comb_length]][,col_i]
    group_names <- as.character(seq_along(filtering_groups))
    
    
    # prepare grid of all combinations
    filtering_combs <- c(filtering_groups, rep(list(0:1), length(filtering_groups)))
    names(filtering_combs) <- c(p("vars_", group_names), p("vals_", group_names))
    full_grid <- expand.grid(filtering_combs)
    
    for(ll in 1:nrow(full_grid)){ # for each line in the full_grid
      # find df lines that correspond
      cond <- as.logical(rep(TRUE, nrow(df)))
      for(grp in group_names){
        cond <- cond & df[[full_grid[p("vars_", grp)][ll,]]] == full_grid[p("vals_", grp)][ll,]
      }
      # and compute whatever
      full_grid$lines[ll] <- paste(which(cond), collapse = ", ") #for visual verification
      full_grid$n[ll] <- length(df$Size[cond])
      full_grid$sum[ll] <- sum(df$Size[cond])
      full_grid$mean[ll] <- mean(df$Size[cond])
    }
    res[[comb_length]][[col_i]] <- full_grid
    
  }
}
#}, times = 10) #microbenchmark

bind_rows(res) %>% relocate(starts_with("vars") | starts_with("vals"))
0 голосов
/ 07 августа 2020

После обсуждения в комментариях, я думаю, что мы можем рассматривать группы как переменные. Итак, нам нужно изменить форму фрейма данных, чтобы у нас был один столбец на фактор, тогда мы можем использовать стандартные подходы tidyverse. Я предполагаю, что группы определены именами столбцов (A1 ... Ak, B1 ... Bk, ...).

library(tidyverse)
df <- tribble(~Size, ~A1, ~A2, ~B1, ~B2,
              1, "1", "1", "0", "0",
              5, "0", "0", "1", "0",
              10, "1", "1", "1", "0",
              3, "1", "0", "0", "0",
              2, "1", "1", "1", "1",
              55, "0", "0", "0", "1",
              5, "1", "0", "1", "1",
              2, "0", "0", "1", "1",
              1, "1", "1", "1", "1",
              4, "1", "1", "1", "0")

get_levels <- function(col){
  paste(names(col)[col == "1"], collapse = ",")
}
# Rewrite with groups as factors
df_factors <- df %>%
  mutate(id = row_number()) %>%  #to avoid aggregating same Size
  nest(A = starts_with("A"), B = starts_with("B")) %>%
  mutate(A = factor(map_chr(A, get_levels)),
         B = factor(map_chr(B, get_levels)))

# Now look at factor combinations
df_factors %>%
  group_by(A, B) %>%
  summarize(n = n(),
            mean = mean(Size))

# A tibble: 8 x 4
# Groups:   A [3]
#   A       B           n  mean
#   <fct>   <fct>   <int> <dbl>
# 1 ""      "B1"        1   5  
# 2 ""      "B1,B2"     1   2  
# 3 ""      "B2"        1  55  
# 4 "A1"    ""          1   3  
# 5 "A1"    "B1,B2"     1   5  
# 6 "A1,A2" ""          1   1  
# 7 "A1,A2" "B1"        2   7  
# 8 "A1,A2" "B1,B2"     2   1.5

Я явно назвал «A» и «B». Кажется, все еще можно сделать это с 6 группами. Если у вас есть больше, необходимо автоматизировать, но я не знаю, как это легко сделать.

...