从一组集中查找所有不相交(非重叠)集

时间:2013-05-21 16:34:16

标签: r algorithm subset set data-partitioning

我的问题:需要从一组集中找到所有不相交(非重叠)的集合。

背景:我正在使用比较系统发育方法来研究鸟类的性状进化。我有一棵约300种树。该树可以分为子条(即子树)。如果两个子类不共享物种,它们是独立的。我正在寻找一种算法(如果可能的话,还有R实现),它将找到所有可能的子段分区,其中每个子段都有超过10个分类,并且都是独立的。每个子片段都可以被认为是一个集合,当两个子片段是独立的(不共享物种)时,这些子片段就是不相交的集合。

希望这很清楚,有人可以提供帮助。

干杯, 格伦

以下代码生成示例数据集。子序列是所有可能的子段(集)的列表,我想从中取样X个不相交的集合,其中集合的长度为Y.

###################################
# Example Dataset
###################################

    library(ape)
    library(phangorn)
    library(TreeSim)
    library(phytools)

    ##simulate a tree

    n.taxa <- 300
    tree <- sim.bd.taxa(n.taxa,1,lambda=.5,mu=0)[[1]][[1]]
    tree$tip.label <- seq(n.taxa)

    ##extract all monophyletic subclades

    get.all.subclades <- function(tree){
    tmp <- vector("list")
    nodes <- sort(unique(tree$edge[,1]))
    i <- 282
    for(i in 1:length(nodes)){
    x <- Descendants(tree,nodes[i],type="tips")[[1]]
    tmp[[i]] <- tree$tip.label[x]
    }
    tmp
    }
    tmp <- get.all.subclades(tree)

    ##set bounds on the maximum and mininum number of tips of the subclades to include

    min.subclade.n.tip <- 10
    max.subclade.n.tip <- 40


    ##function to replace trees of tip length exceeding max and min with NA

    replace.trees <- function(x, min, max){
    if(length(x) >= min & length(x)<= max) x else NA
    }


    #apply testNtip across all the subclades

    tmp2 <- lapply(tmp, replace.trees, min = min.subclade.n.tip, max = max.subclade.n.tip)

    ##remove elements from list with NA, 
    ##all remaining elements are subclades with number of tips between 
##min.subclade.n.tip and max.subclade.n.tip

    subclades <- tmp2[!is.na(tmp2)]

    names(subclades) <- seq(length(subclades))

2 个答案:

答案 0 :(得分:2)

以下是一个示例,说明如何测试每对列表元素的零重叠,提取所有非重叠对的索引。

findDisjointPairs <- function(X) {
    ## Form a 2-column matrix enumerating all pairwise combos of X's elements
    ij <- t(combn(length(X),2))    
    ## A function that tests for zero overlap between a pair of vectors
    areDisjoint <- function(i, j) length(intersect(X[[i]], X[[j]])) == 0     
    ## Use mapply to test for overlap between each pair and extract indices 
    ## of pairs with no matches
    ij[mapply(areDisjoint, ij[,1], ij[,2]),]
}

## Make some reproducible data and test the function on it
set.seed(1)
A <- replicate(sample(letters, 5), n=5, simplify=FALSE)    
findDisjointPairs(A)
#      [,1] [,2]
# [1,]    1    2
# [2,]    1    4
# [3,]    1    5

答案 1 :(得分:1)

以下是一些可能有用的功能。

第一个计算集合列表的所有可能的不相交集合。

我正在使用“集合”而不是“分区”,因为集合不一定涵盖宇宙(即所有集合的集合)。

该算法是递归的,仅适用于少量可能的集合。这并不一定意味着它不适用于大型集合列表,因为该函数在每次迭代时都会删除相交集。

如果代码不清楚,请询问,我会添加评论。

输入必须是名为的列表,结果将是一个集合列表,它是一个表示集合名称的字符向量。

DisjointCollectionsNotContainingX <- function(L, branch=character(0), x=numeric(0))
{
    filter <- vapply(L, function(y) length(intersect(x, y))==0, logical(1))

    L <- L[filter]

    result <- list(branch)

    for( i in seq_along(L) )
    {
        result <- c(result, Recall(L=L[-(1:i)], branch=c(branch, names(L)[i]), x=union(x, L[[i]])))
    }

    result
}

这只是隐藏辅助参数的包装器:

DisjointCollections <- function(L) DisjointCollectionsNotContainingX(L=L)

下一个函数可用于验证给定的非重叠和“最大”集合的给定列表。

对于每个集合,它将测试是否
1.所有套装实际上是不相交的 2.添加另一个集合会导致不相交的集合或现有集合:

ValidateDC <- function(L, DC)
{
    for( collection in DC )
    {
        for( i in seq_along(collection) )
        {
            others <- Reduce(f=union, x=L[collection[-i]])

            if( length(intersect(L[collection[i]], others)) > 0 ) return(FALSE)
        }

        elements <- Reduce(f=union, x=L[collection])

        for( k in seq_along(L) ) if( ! (names(L)[k] %in% collection) )
        {
            if( length(intersect(elements, L[[k]])) == 0 )
            {
                check <- vapply(DC, function(z) setequal(c(collection, names(L)[k]), z), logical(1))

                if( ! any(check) ) return(FALSE)
            }
        }
    }

    TRUE
}

示例:

L <- list(A=c(1,2,3), B=c(3,4), C=c(5,6), D=c(6,7,8))

> ValidateDC(L,DisjointCollections(L))
[1] TRUE

> DisjointCollections(L)
[[1]]
character(0)

[[2]]
[1] "A"

[[3]]
[1] "A" "C"

[[4]]
[1] "A" "D"

[[5]]
[1] "B"

[[6]]
[1] "B" "C"

[[7]]
[1] "B" "D"

[[8]]
[1] "C"

[[9]]
[1] "D"

请注意,包含AB的集合同时不会显示,因为它们的非空交集。此外,同时显示CD的集合。其他人都可以。

注意:空集合character(0)始终是有效组合。

创建所有可能的不相交集合后,您可以应用任何要继续的过滤器。


编辑:

  1. 我从第一个函数中删除了行if( length(L)==0 ) return(list(branch));它不需要。

  2. 性能:如果集合之间存在相当大的重叠,则该函数运行速度很快。例如:

    set.seed(1)

    L&lt; - lapply(1:50,函数(。)样本(x = 1200,大小= 20))

    姓名(L)&lt; - c(字母,字母)[1:50]

    system.time(DC&lt; - DisjointCollections(L))

  3. 结果:

    #   user  system elapsed 
    #   9.91    0.00    9.92
    

    找到的馆藏总数:

    > length(DC)
    [1] 121791