Итерация по всем парам столбцов df и подсчет ненулевых пересечений - PullRequest
2 голосов
/ 29 февраля 2020

У меня есть ~ 15000 * 1000 фрейм данных, где каждый столбец представляет отдельную личность, а каждая строка представляет частоту (0 или 1).

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

В настоящее время я перебираю все столбцы с помощью combn и вставляю взаимные имена строк в строку. То есть у меня есть решение, однако оно очень и очень медленное (вероятно, квадратичное c с номером столбца).

Есть ли способ векторизовать эту проблему / подойти к ней с помощью tidyr / dplyr et c.? Кажется, я не могу найти способ.

Например:

------|individual1 | individual2 | individual3 | ...
trait1|     0      |     1       |      1      | ...
trait2|     0      |     0       |      0      | ...
trait3|     1      |     1       |      1      | ...
 ...  |    ...     |    ...      |     ...     | ... 

Возвращает строку trait1,trait3 для пары individual 2 и individual 3.

Спасибо!


Данные о игрушках (фактические данные слишком малы, чтобы вытащить подмножество):

df <- data.frame(trait = c("a", "b", "c", "d", "e"), ind1 = c(0, 1, 1, 0, 1), ind2 = c(1, 0, 1, 0, 1), ind3 = c(1, 0, 1, 1, 1))

Ответы [ 2 ]

0 голосов
/ 01 марта 2020

Хотя этот вопрос имеет принятый ответ , я хотел бы предложить другой подход, который использует dplyr и tidyr, а также вариант data.table.

Всякий раз, когда бы Имена столбцов обрабатываются как элементы данных, это указывает на то, что набор данных хранится в неопрятном формате, IMHO. Преобразование данных в длинный формат позволит применять обычные манипуляции с данными, такие как объединение, группирование, агрегирование.

dplyr и tidyr

library(dplyr)
library(tidyr)
df %>% 
  pivot_longer(!"trait") %>% 
  filter(value == 1L) %>% 
  select(-value) %>% 
  inner_join(., ., by = "trait") %>% 
  filter(name.x < name.y) %>% 
  group_by(name.x, name.y) %>% 
  summarise(traits = toString(trait)) %>% 
  ungroup()
# A tibble: 3 x 3
  name.x name.y  traits 
  <chr>  <chr>   <chr>  
1 Alice  Bob     c, e   
2 Alice  Charlie c, e   
3 Bob    Charlie a, c, e

Пояснение

df %>% 
  pivot_longer(!"trait") %>% 
  filter(value == 1L)

преобразует данные в длинный формат, который является компактным представление исходной матрицы в широком формате:

# A tibble: 10 x 3
   trait name    value
   <fct> <chr>   <dbl>
 1 a     Bob         1
 2 a     Charlie     1
 3 b     Alice       1
 4 c     Alice       1
 5 c     Bob         1
 6 c     Charlie     1
 7 d     Charlie     1
 8 e     Alice       1
 9 e     Bob         1
10 e     Charlie     1

Столбец value удален, поскольку он больше не нужен. Затем длинные данные объединяются вместе, чтобы найти все имена, которые совпадают с trait. Результат включает пары имен, которые даны в другом порядке, например, (Алиса, Боб) и (Боб, Алиса), а также дубликаты имен, например, (Боб, Боб). Они удаляются.

Наконец, данные группируются и суммируются.

data.table

Вариант data.table реализует тот же подход, но имеет преимущество, позволяющее самоэквивалентное неравенство , которое уменьшает количество строк непосредственно в объединении вместо последующего этапа фильтрации.

library(data.table)
long <- melt(setDT(df), id.vars = "trait", variable.name = "name")[value == 1]
long[long, on = .(trait, name < name), .(name1 = x.name, name2 = i.name, trait), nomatch = NULL][
  , .(traits = toString(trait)), keyby = .(name1, name2)]
   name1   name2  traits
1: Alice     Bob    c, e
2: Alice Charlie    c, e
3:   Bob Charlie a, c, e
0 голосов
/ 29 февраля 2020

Попробуйте применить пользовательскую функцию к каждой комбинации столбцов. Может быть, эффективность можно немного улучшить.

t(combn(1:(ncol(df)-1), 2, function(x){
  string <- paste(df$trait[df[[x[1]+1]] == 1 & df[[x[2]+1]] == 1], collapse = ",")
  c(names(df)[x+1], string)
}))

#      [,1]    [,2]      [,3]   
# [1,] "Alice" "Bob"     "c,e"  
# [2,] "Alice" "Charlie" "c,e"  
# [3,] "Bob"   "Charlie" "a,c,e"

Данные

df <- data.frame(trait = c("a", "b", "c", "d", "e"),
                 Alice = c(0, 1, 1, 0, 1),
                 Bob = c(1, 0, 1, 0, 1),
                 Charlie = c(1, 0, 1, 1, 1))
...