如何在R中识别和计算交集项

4
我有一个数据框,显示三个颜色类别的会员情况。数字代表唯一的 ID。一个 ID 可能属于一个或多个组。
dat <- data.frame(BLUE = c(1, 2, 3, 4, 6, NA),
                  RED = c(2, 3, 6, 7, 9, 13),
                  GREEN = c(4, 6, 8, 9, 10, 11))

或者为了视觉参考:

BLUE  RED  GREEN
1     2    4
2     3    6
3     6    8
4     7    9
6     9    10
NA    13   11

我需要识别和统计个人和跨组成员身份(即红色中有多少个ID,红色和蓝色中有多少个ID等)。 我期望的输出如下所示。 请注意,IDs列仅供参考,该列不会出现在预期的输出中。

COLOR                TOTAL  IDs (reference only, not needed in final output)
RED                  2      (7, 13)
BLUE                 1      (1)
GREEN                3      (8, 10, 11)
RED, BLUE            3      (2, 3, 6)
RED, GREEN           2      (6, 9)
BLUE, GREEN          2      (4, 6)
RED, BLUE, GREEN     1      (6)

有没有人知道在R中高效完成这个的方法?谢谢!


为什么7和13是红色的ID? - NelsonGon
1
数字2指的是仅在红色组中出现的ID数量。由于7和13只在红色组中出现,而不在蓝色或绿色组中,因此红色组的总数为2。红色组中的所有其他数字都出现在其他组中。7和13只是我为说明目的而创建的随机数字,但它们与实际数据集中出现的ID具有相同的特征(即有些只在一个组中出现,有些则在多个组中出现)。 - DJC
为什么数字6在RED, BLUE, GREEN中以及所有3个成对组中都被列出?我理解为您只想要每个ID被计算一次(在其最大组中)。 - ClancyStats
不确定我是否理解问题,但是数字6分别列在红色、蓝色和绿色行中,因为我需要能够确定同时在这三个组中有多少个ID。 - DJC
2个回答

6
您可以使用 venn 库(特别适用于您的数据中没有 NAs 的情况):
venn_table <- venn(as.list(dat))

               BLUE RED GREEN counts
                  0   0     0      0
GREEN             0   0     1      3
RED               0   1     0      2
RED:GREEN         0   1     1      1
BLUE              1   0     0      2
BLUE:GREEN        1   0     1      1
BLUE:RED          1   1     0      2
BLUE:RED:GREEN    1   1     1      1

并且:

attr(venn_table, "intersections")

$GREEN
[1]  8 10 11

$RED
[1]  7 13

$`RED:GREEN`
[1] 9

$BLUE
[1]  1 NA

$`BLUE:GREEN`
[1] 4

$`BLUE:RED`
[1] 2 3

$`BLUE:RED:GREEN`
[1] 6

为了包括ID:

data.frame(venn_table[2:nrow(venn_table), ],
           ID = do.call("rbind", lapply(attr(venn_table, "intersections"), paste0, collapse = ",")))

               BLUE RED GREEN counts      ID
GREEN             0   0     1      3 8,10,11
RED               0   1     0      2    7,13
RED:GREEN         0   1     1      1       9
BLUE              1   0     0      2    1,NA
BLUE:GREEN        1   0     1      1       4
BLUE:RED          1   1     0      2     2,3
BLUE:RED:GREEN    1   1     1      1       6

处理NAs的一种方法:

venn_table2 <- data.frame(venn_table[2:nrow(venn_table), length(venn_table), drop = FALSE],
                          ID = do.call("rbind", lapply(attr(venn_table, "intersections"), paste0, collapse = ",")))

counts <- venn_table2[1] - with(venn_table2, lengths(regmatches(ID, gregexpr("NA", ID))))

               counts
GREEN               3
RED                 2
RED:GREEN           1
BLUE                1
BLUE:GREEN          1
BLUE:RED            2
BLUE:RED:GREEN      1

一个更优雅的处理缺失值的方法可能是(基于@M--的评论):

print(venn(Map(function(x) x[!is.na(x)], as.list(dat))))

               BLUE RED GREEN counts
                  0   0     0      0
GREEN             0   0     1      3
RED               0   1     0      2
RED:GREEN         0   1     1      1
BLUE              1   0     0      1
BLUE:GREEN        1   0     1      1
BLUE:RED          1   1     0      2
BLUE:RED:GREEN    1   1     1      1

@M,感谢您指出这一点。实际上,我在最终输出中不需要ID列,所以不用担心这个问题。这种方法更简单,但我注意到它将NA值视为唯一值进行计数(因此蓝色的总数是2,而不是1),您有什么解决办法吗? - DJC

2
library(dplyr)
library(tidyr)

cbind(dat, row = 1:6) %>% 
  gather(COLOR, IDs, -row) %>% 
  group_by(IDs) %>% 
  nest(COLOR, .key="COLOR") %>% 
  mutate(COLOR = sapply(COLOR, as.character)) %>% 
  drop_na %>% 
  group_by(COLOR) %>% 
  add_count(name="TOTAL") %>% 
  group_by(COLOR, TOTAL) %>% 
  nest(IDs, .key = "IDs") %>% 
  as.data.frame

#>                       COLOR TOTAL       IDs
#> 1                      BLUE     1         1
#> 2          c("BLUE", "RED")     2      2, 3
#> 3        c("BLUE", "GREEN")     1         4
#> 4 c("BLUE", "RED", "GREEN")     1         6
#> 5                       RED     2     7, 13
#> 6         c("RED", "GREEN")     1         9
#> 7                     GREEN     3 8, 10, 11

venn包中,有一种更传统的方法来处理NA:


library(purrr)
library(magrittr)
library(venn)

as.list(dat) %>%
  map(discard, is.na) %>%
  compact() %>% 
  venn() %>% 
  print

    #>                BLUE RED GREEN counts
    #>                   0   0     0      0
    #> GREEN             0   0     1      3
    #> RED               0   1     0      2
    #> RED:GREEN         0   1     1      1
    #> BLUE              1   0     0      1
    #> BLUE:GREEN        1   0     1      1
    #> BLUE:RED          1   1     0      2
    #> BLUE:RED:GREEN    1   1     1      1

根据这个答案,在R中有许多其他用于绘制venn图的包。

例如,VennDiagram::venn.diagram包有一个na变量,可以选择stopremovenone。因此,在这里,我们将使用remove;但是,它只会给我们提供图表而不是表格。您可以在其他包中探索其他可能性。


同意。不管是谁做的都不好。抱歉,我对@M和@tmfmnk的解决方案有一个后续问题。在你们两个的解决方案中,像RED:GREEN = 1或RED:BLUE = 2这样的组合,但它们不应该分别返回2和3吗?也许我疯了,但我重新计算了一下,它们应该是这样返回的,不是吗? - DJC
1
@DJC 不可以,因为数字 6 同时在 红、绿、蓝 三个集合中。你这样算会重复计算。如果按照你的逻辑,除了 红、绿 = 2红、蓝 = 3蓝、绿 也应该是 2 而不是 1。请看我上面的图以获得澄清。另外,每条评论只能标记一个人。tmfmnk 没有被通知到。 - M--
1
@DJC 需要一些时间来考虑。但我认为我可以通过“整洁宇宙”解决方案来使其按照您的意愿工作。 - M--
2
我相信可以用 tidyverse 来完成,但是我认为通过维恩图方法无法有意义地解决这个问题。 - tmfmnk
@DJC 问题。如果你希望有 rg、bg、rb 这些颜色组合,那么你是否也希望这些颜色分别以红色、蓝色和绿色作为基础,这样总共就会有6种(或者蓝色只有5种,因为它有一个NA)?我想表达的是,你的逻辑不一致。 - M--
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接