遍历所有df列对并计算非零交集

时间:2020-02-29 18:17:25

标签: r dataframe dplyr tidyr

我有一个〜15000 * 1000数据帧,其中每一列代表一个个体,每一行代表一个特征(0或1)的发生率。

我想高效地比较所有的列对,并为所有可能的对生成所有相互特征(行名)的逗号分隔列表。

当前,我正在通过combn遍历所有列,并将相互的行名粘贴到字符串中。那就是说我有一个解决方案,但是,它非常非常慢(可能是列数的平方)。

有没有办法向量化这个问题/用tidyr / dplyr等方法解决?我似乎找不到办法。

例如:

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

trait1,trait3individual 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 :(得分:0)

尝试对每个列组合应用自定义功能。也许可以稍微提高效率。

yarn run compile

数据

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"

答案 1 :(得分:0)

尽管这个问题已经接受了answer,但我想提出一种使用dplyrtidyr以及data.table变体的不同方法。

每当将列名视为数据项时,这表明该数据集以不整齐的格式IMHO存储。将数据重塑为长格式将允许应用通常的数据操作,例如连接,分组,聚合。

dplyrtidyr

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匹配的所有名称。结果包括以不同顺序给出的名称对,例如(Alice,Bob)和(Bob,ALice),以及重复的名称,例如(Bob,Bob)。这些已删除。

最后,将数据分组并汇总。

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