How to get counts of intersections of six or more sets?

半城伤御伤魂 提交于 2019-12-01 13:20:03

问题


I am running an analysis of a number of sets and I have been using the package VennDiagram, which has been working just fine, but it only handles up to 5 sets, and now it turns out that I need to look at 6 or more sets.

Ideally, I'm looking for a something that can do this (below) with 6 or more sets, but it doesn't necessarily have to have a plot function as long as the counts can be retrieved:

Any ideas of what I can do to add one or more sets to these five and still get the counts?

Thanks!


回答1:


Here's an attempt:

list1 <- c("a","b","c","e")
list2 <- c("a","b","c","e")
list3 <- c("a","b")
list4 <- c("a","b","g","h")
list_names <- c("list1","list2","list3","list4")

lapply(1:length(list_names),function(y){
combinations <- combn(list_names,y)
res<-as.list(apply(combinations,2,function(x){
    if(length(x)==1){
            p <- setdiff(get(x),unlist(sapply(setdiff(list_names,x),get)))
        }

    else if(length(x) < length(list_names)){
            p <- setdiff(Reduce(intersect,lapply(x,get)),Reduce(union,sapply(setdiff(list_names,x),get)))
        }

    else p <- Reduce(intersect,lapply(x,get))

    if(!identical(p,character(0))) p
    else NA
}))

if(y==length(list_names)) {
        res[[1]] <- unlist(res); 
        res<-res[1]
}
names(res) <- apply(combinations,2,paste,collapse="-")
res
})

The first lapply is used to loop from 1 to the number of sets you have. Then I took all possible combinations of list names, taken y at a time. This essentially generates all of the different subareas in the Venn diagram.

For each combination, the output is the difference between the intersection of the lists in the current combination to the union of the other lists that are not in the combination.

The final result is a list of length the number of sets inputed. The first element of that list holds the unique elements in each list, the second element the unique elements in any combination of two lists etc.




回答2:


Here is a recursive solution to find all of the intersections in the venn diagram. sets can be a list containing any number of sets to find the intersections of. For some reason, the code in the package you are using is all hard-coded for each set size, so it doesn't scale to arbitrary intersections.

## Build intersections, 'out' accumulates the result
intersects <- function(sets, out=NULL) {
    if (length(sets) < 2) return ( out )                               # return result
    len <- seq(length(sets))
    if (missing(out)) out <- list()                                    # initialize accumulator
    for (idx in split((inds <- combn(length(sets), 2)), col(inds))) {  # 2-way combinations
        ii <- len > idx[2] & !(len %in% idx)                           # indices to keep for next intersect
        out[[(n <- paste(names(sets[idx]), collapse="."))]] <- intersect(sets[[idx[1]]], sets[[idx[2]]])
        out <- intersects(append(out[n], sets[ii]), out=out)
    }
    out
}

The function builds pairwise intersections. To avoid building repeated solutions it only calls itself on components of the set with indices greater than those that were joined (ii in the code). The result is a list of all the intersections. If you pass named components, then the result will be named by the convention "set1.set2" etc.

Results

## Some sample data
set.seed(0)
sets <- setNames(lapply(1:3, function(.) sample(letters, 10)), letters[1:3])

## Manually check intersections
a.b <- intersect(sets[[1]], sets[[2]])
b.c <- intersect(sets[[2]], sets[[3]])
a.c <- intersect(sets[[1]], sets[[3]])
a.b.c <- intersect(a.b, sets[[3]])

## Compare
res <- intersects(sets)
all.equal(res[c("a.b","a.c","b.c","a.b.c")], list(a.b=a.b, a.c=a.c, b.c=b.c, a.b.c=a.b.c))
# TRUE

res
# $a.b
# [1] "g" "i" "n" "e" "r"
# 
# $a.b.c
# [1] "g"
# 
# $a.c
# [1] "x" "g"
# 
# $b.c
# [1] "f" "g"

## Get the counts of intersections
lengths(res)
# a.b a.b.c   a.c   b.c 
#   5     1     2     2 

Or, with numbers

intersects(list(a=1:10, b=c(1, 5, 10), c=9:20))
# $a.b
# [1]  1  5 10
# $a.b.c
# [1] 10
# $a.c
# [1]  9 10
# $b.c
# [1] 10



回答3:


OK, here's one way, assuming you represent sets as a list of vectors, and items to be searched in those sets also as vector:

# Example data format
sets <- list(v1 = 1:6, v2 = 1:8, v3 = 3:8)
items <- c(2:7)

# Search for items in each set
result <- data.frame(searched = items)
for (set in names(sets)) {
  result <- cbind(result, items %in% sets[[set]])
  names(result)[length(names(result))] <- set
}

# Count
library(plyr)
ddply(result, names(sets), function (i) {
  data.frame(count = nrow(i))
})

This gives you all combinations actually existing in the itemset:

     v1   v2    v3 count
1 FALSE TRUE  TRUE     1
2  TRUE TRUE FALSE     1
3  TRUE TRUE  TRUE     4


来源:https://stackoverflow.com/questions/31604136/how-to-get-counts-of-intersections-of-six-or-more-sets

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!