#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