Использование “combn” для создания списка кадров данных для всех выбранных комбинаций переменных и столбцов для не выбранных переменных

#r #combinations

Вопрос:

Из приведенного ниже фрейма данных z я хотел бы сгенерировать список новых фреймов данных, в которых каждый фрейм данных основан на уникальной комбинации 3 различных значений вектора имен. В приведенном ниже фрейме данных у меня есть 5 разных имен в столбце Имя (у них несколько записей репликации). Количество различных комбинаций из 3 равно 10 (при выборке без замены и порядка не важно). хотя выбор основан на векторе имени, я бы хотел, чтобы новые кадры данных включали другие столбцы информации. В данном случае в графе «Голосование». Я также хотел бы, чтобы помимо этих двух столбцов каждый фрейм данных содержал два дополнительных столбца со строками, которые не были выбраны. Например, если вы запустите код ниже первого кадра данных в combdf, будет содержать Джона, Ли, Сьюзен и их голоса. Но я не мог найти/понять, как добавить в этот фрейм данных два столбца для оставшихся двух имен и их голосов, и так далее для остальных из них. В этих двух столбцах будет меньше строк, поэтому я согласен с NA для недостающих ячеек.

 Name <- c("Jhon", "Lee", "Suzan", "Abhinav",
      "Brain")
Vote <- letters[1:21]
z <- as.data.frame (cbind(Name, Vote))
comb<-combn(unique(as.character(z$Name)), 3)
combdf <- apply(comb, 2, function(vec) z[ z$Name %in% vec, ] )
 

Ответ №1:

Один из более простых вариантов-использовать bind_rows filter ed с » vec » в «Имени» и вторыми данными без них, переименовать его так, чтобы он создавал новые столбцы, заполненные NA

 library(dplyr)
out <- z %>%
          pull(Name) %>%
          unique %>%
         combn(., 3, FUN = function(vec) 
          z %>%
           filter(Name %in% vec) %>%
           bind_rows(z %>% 
                   filter(!Name %in% vec) %>% 
                   rename(Name2 = Name, Vote2 = Vote)), simplify = FALSE)
 

-выход

 out[[1]]
#    Name Vote   Name2 Vote2
#1   Jhon    a    <NA>  <NA>
#2    Lee    b    <NA>  <NA>
#3  Suzan    c    <NA>  <NA>
#4   Jhon    f    <NA>  <NA>
#5    Lee    g    <NA>  <NA>
#6  Suzan    h    <NA>  <NA>
#7   Jhon    k    <NA>  <NA>
#8    Lee    l    <NA>  <NA>
#9  Suzan    m    <NA>  <NA>
#10  Jhon    p    <NA>  <NA>
#11   Lee    q    <NA>  <NA>
#12 Suzan    r    <NA>  <NA>
#13  Jhon    u    <NA>  <NA>
#14  <NA> <NA> Abhinav     d
#15  <NA> <NA>   Brain     e
#16  <NA> <NA> Abhinav     i
#17  <NA> <NA>   Brain     j
#18  <NA> <NA> Abhinav     n
#19  <NA> <NA>   Brain     o
#20  <NA> <NA> Abhinav     s
#21  <NA> <NA>   Brain     t
 

Кроме того, если нам нужно, чтобы NA внизу

 out2 <- z %>%
          pull(Name) %>%
          unique %>%
         combn(., 3, FUN = function(vec) 
          z %>%
           filter(Name %in% vec) %>%
           bind_rows(z %>% 
                   filter(!Name %in% vec) %>% 
                   rename(Name2 = Name, Vote2 = Vote)) %>%
           mutate(across(c(Name2, Vote2),
             ~ .[order(is.na(.))])), simplify = FALSE)



out2[[1]]
#    Name Vote   Name2 Vote2
#1   Jhon    a Abhinav     d
#2    Lee    b   Brain     e
#3  Suzan    c Abhinav     i
#4   Jhon    f   Brain     j
#5    Lee    g Abhinav     n
#6  Suzan    h   Brain     o
#7   Jhon    k Abhinav     s
#8    Lee    l   Brain     t
#9  Suzan    m    <NA>  <NA>
#10  Jhon    p    <NA>  <NA>
#11   Lee    q    <NA>  <NA>
#12 Suzan    r    <NA>  <NA>
#13  Jhon    u    <NA>  <NA>
#14  <NA> <NA>    <NA>  <NA>
#15  <NA> <NA>    <NA>  <NA>
#16  <NA> <NA>    <NA>  <NA>
#17  <NA> <NA>    <NA>  <NA>
#18  <NA> <NA>    <NA>  <NA>
#19  <NA> <NA>    <NA>  <NA>
#20  <NA> <NA>    <NA>  <NA>
#21  <NA> <NA>    <NA>  <NA>
 

Или также может использовать setdiff/anti_join от dplyr

 out <- z %>% 
   pull(Name) %>% 
   unique %>% 
   combn(., 3, FUN = function(vec) {
             z1 <- z %>%
                       filter(Name %in% vec)
             z2 <- setdiff(z, z1)
             names(z2) <- paste0(names(z2), 2)
             bind_rows(z1, z2)
             }, simplify = FALSE)
 

Ответ №2:

 f <- function(df,n)
{ # creates n NA rows
  naDF = df[1,]
  naDF[1,] <- NA
  naDF[rep(seq_len(nrow(naDF)), each = n), ]
}
# previous code unchanged

df <- lapply(1:dim(comb)[2], function(x) {df1 = z[ z$Name %in% comb[,x], ]; df2 = z[ !z$Name %in% comb[,x], ]; cbind(df1, rbind(df2, f(df2, nrow(df1)-nrow(df2))))})

> df[[1]]
    Name Vote    Name Vote
1   Jhon    a Abhinav    d
2    Lee    b   Brain    e
3  Suzan    c Abhinav    i
6   Jhon    f   Brain    j
7    Lee    g Abhinav    n
8  Suzan    h   Brain    o
11  Jhon    k Abhinav    s
12   Lee    l   Brain    t
13 Suzan    m    <NA> <NA>
16  Jhon    p    <NA> <NA>
17   Lee    q    <NA> <NA>
18 Suzan    r    <NA> <NA>
21  Jhon    u    <NA> <NA>