Преобразование двоичного фрейма данных в сгруппированный (длинный) список комбинаций

#r

#r

Вопрос:

У меня есть следующий двоичный фрейм данных

 A    B    C    D
0    1    1    0
0    0    1    1
1    1    1    0
0    1    1    1
 

Я хотел бы создать список со всеми комбинациями столбцов и подсчитать общие строки с «1».

Точнее, что-то вроде этого:

 A    B    1
A    C    1
A    D    0
B    A    1
B    C    3
B    D    1
C    A    1
C    B    3
C    D    2
D    A    0
D    B    1
D    C    2
 

Но я изо всех сил пытаюсь придумать, как сделать это в R. Я был бы признателен за любой намек в правильном направлении

В качестве альтернативы, для меня подойдет «корреляционная» матрица. Например:

     A    B    C    D
A   0    1    1    0
B   1    0    3    1
C   1    3    0    2
D   0    1    2    0
 
 

Ответ №1:

Поскольку я не очень хорошо понимаю purrr / apply / loops, мой подход будет таким

 library(tidyverse)
df %>%
  mutate(id = row_number()) %>% 
  pivot_longer(cols = 1:4) %>%
  left_join(df %>% mutate(id = row_number())) %>%
  pivot_longer(cols = 4:7, names_to = "Name2", values_to = "Value2") %>%
  filter(name != Name2, value == Value2) %>%
  select(-1) %>% group_by(name, Name2) %>%
  summarise(sum(value))

# A tibble: 12 x 3
# Groups:   name [4]
   name  Name2 `sum(value)`
   <chr> <chr>        <int>
 1 A     B                1
 2 A     C                1
 3 A     D                0
 4 B     A                1
 5 B     C                3
 6 B     D                1
 7 C     A                1
 8 C     B                3
 9 C     D                2
10 D     A                0
11 D     B                1
12 D     C                2
 

Объяснение Преобразование его в длинный формат, затем объединение с оригиналом с учетом идентификаторов строк, затем снова pivot_longer, отфильтровывание одинаковых имен и разных значений даст вам желаемые комбинации, которые при суммировании в виде суммы значений (оба равны) дают желаемый результат

Комментарии:

1. Это работает, как и ожидалось, спасибо. Было бы большим изменением также добавить комбинации автоциклов, например, A — A -> 0, B — B -> 0 и т.д.?

2. Нет, я так не думаю. Не фильтруйте одноименные комбинации и не заменяйте их значения на 0, используя mutate и ifelse

3. После этого его можно легко преобразовать в матричную форму с помощью pivot wider

Ответ №2:

Один gtools , dplyr и purrr опция может быть:

 map_dfr(.x = asplit(permutations(length(df), 2, names(df)), 1),
        ~ df %>%
         summarise(pair = paste(.x, collapse = ","),
                   n = sum(rowSums(select(., all_of(.x))) == 2)))

   pair n
1   A,B 1
2   A,C 1
3   A,D 0
4   B,A 1
5   B,C 3
6   B,D 1
7   C,A 1
8   C,B 3
9   C,D 2
10  D,A 0
11  D,B 1
12  D,C 2
 

Ответ №3:

Чисто базовый вариант R выглядит следующим образом. Обратите внимание, что это дает только уникальные комбинации столбцов. Вы получаете более длинную версию всех перестановок, изменяя порядок столбцов и копируя подсчитанные значения.

Пример данных

 test <- data.frame(A = c(0, 0, 1, 0), 
                   B = c(1, 0, 1, 1), 
                   C = c(1,1,1,1), 
                   D = c(0, 1, 0, 1))
 

Код

 df_list <- lapply(1:(ncol(combn(1:ncol(test), m = 2))), 
                  function(y) test[, combn(1:ncol(test), m = 2)[,y]]) 
values <- sapply(df_list, function(x) sum(apply(x, 1, sum) == 2))
names <- sapply(df_list, function(x) colnames(x))
df_final <- cbind.data.frame(t(names), values)
 

Вывод

 > df_final
  1 2 values
1 A B      1
2 A C      1
3 A D      0
4 B C      3
5 B D      1
6 C D      2
 

Ответ №4:

Базовая опция R, использующая expand.grid subset

 transform(
  subset(
    rev(
      expand.grid(nm <- names(df), nm)
    ), Var1 != Var2
  ),
  count = apply(
    cbind(Var2, Var1),
    1,
    function(...) sum(do.call("*", df[...]))
  )
)
 

дает

    Var2 Var1 count
2     A    B     1
3     A    C     1
4     A    D     0
5     B    A     1
7     B    C     3
8     B    D     1
9     C    A     1
10    C    B     3
12    C    D     2
13    D    A     0
14    D    B     1
15    D    C     2
 

Ответ №5:

Я бы предложил использовать crossprod . Здесь я добавил diag , чтобы установить диагональ равной нулю:

 "diag<-"(crossprod(as.matrix(test)), 0)
#   A B C D
# A 0 1 1 0
# B 1 0 3 1
# C 1 3 0 2
# D 0 1 2 0
 

Чтобы получить длинную форму, вы можете добавить пару шагов:

 mat <- "diag<-"(crossprod(as.matrix(test)), 0)
df <- data.frame(as.table(mat))
subset(df[order(df$Var1), ], Var1 != Var2)
#    Var1 Var2 Freq
# 5     A    B    1
# 9     A    C    1
# 13    A    D    0
# 2     B    A    1
# 10    B    C    3
# 14    B    D    1
# 3     C    A    1
# 7     C    B    3
# 15    C    D    2
# 4     D    A    0
# 8     D    B    1
# 12    D    C    2
 

Это более компактно, используя «data.table»:

 library(data.table)
mat <- "diag<-"(crossprod(as.matrix(test)), 0)
data.table(as.table(mat))[V1 != V2][order(V1)]
#     V1 V2 N
#  1:  A  B 1
#  2:  A  C 1
#  3:  A  D 0
#  4:  B  A 1
#  5:  B  C 3
#  6:  B  D 1
#  7:  C  A 1
#  8:  C  B 3
#  9:  C  D 2
# 10:  D  A 0
# 11:  D  B 1
# 12:  D  C 2