【问题标题】:find all disjoint (non-overlapping) sets from a set of sets从一组集合中找到所有不相交(不重叠)的集合
【发布时间】:2013-05-21 16:34:16
【问题描述】:

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

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

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

干杯, 格伦

以下代码生成示例数据集。其中 subclades 是所有可能的子分支(集合)的列表,我想从中采样 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))

【问题讨论】:

  • 因此您将每个子树视为一个子集,并且您希望找到一个子集的集合,使得 each 元素只包含一次;或者你不在乎是否遗漏了一些元素?
  • 如果您的意思是“恰好一次”的解释,那么这就是Exact cover problem。它是 NP 完全的,这意味着您不会比简单地回溯和测试所有可能性做得更好。
  • 我想说的是,en.wikipedia.org/wiki/Knuth%27s_Algorithm_X 听起来像是个问题。
  • 如果您的数据集不是特别大,则首先收集所有length(clade.x) &gt;=10,然后对该子集的所有组合运行intersect
  • 与@CarlWitthoft 一致,您可以执行i &lt;- 3; j &lt;- 4; length(intersect(A[[i]],A[[j]]))&gt;0 之类的操作

标签: r algorithm subset set data-partitioning


【解决方案1】:

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

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

【讨论】:

  • 他仍然需要过滤他想要的元素大小,否则这看起来很不错。
  • @CarlWitthoft -- 是的。不过,这很简单,我会让 OP 将其添加到他/她的工作流程中最适合的地方。 (人们总是可以在areDisjoint 的正文中添加length(X[[i]] &gt;= 10 &amp; length(X[[j]]) &gt;= 10 &amp; 的单行,但我不知道这个函数是否需要针对速度进行优化,在这种情况下我会做一些更复杂的事情。)
  • 嗨@JoshO'Brien。感谢 cmets 和功能,效果很好。现在的问题是从集合的宇宙中找到所有可能的组合。当要计算的可能组合的数量变大时,例如选择(50,20),我的电脑没有足够的内存。有没有办法从一个非常大的可能组合列表中随机抽取组合?像combn(50,20)这样的东西。如果我能做到这一点,那么我只是随机选择集合的组合,直到循环为我的分析积累了足够数量的独立子分支的随机集合。谢谢!
  • @user1322491 -- 如果我理解您的问题,这里的想法应该会有所帮助。 replicate(5, sample(1:50, size=20))(此示例为您提供了从 1 到 50 之间选择的五组 20 个整数,然后您可以将它们用作子集列表的索引。)
【解决方案2】:

这里有一些可能有用的功能。

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

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

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

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

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

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)

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

对于每个集合,它都会测试 if
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

    名称(L)

    system.time(DC

结果:

#   user  system elapsed 
#   9.91    0.00    9.92

找到的集合总数:

> length(DC)
[1] 121791

【讨论】:

  • 感谢您的功能。我试了一下,它们适用于长度 40 的大列表上尝试了它,但 R 崩溃了。我可能会扩大我的分析,并且需要一个可以处理长度> 50的集合列表的函数。我应该澄清一下,我最感兴趣的是从长度 > X 的不相交集合列表中随机抽样。X 可能是 8 或更多。我承认我不太了解您的 Recall() 函数是如何工作的,但是无论如何修改它是否只对长度 > X 的不相交集合进行采样?谢谢!格伦
  • 嗨格伦@user1322491。 Recall 是在其中调用它的函数的简写。这是递归函数的惯用语。这些功能可以进一步优化。但我认为它们适用于大量集合在集合之间有相当大的重叠。不幸的是,情况似乎并非如此。一些问题: 1. 在你的系列中重叠很少见吗? 2. R 挂起或只是用完列表> 40 的内存? 3. 随机抽样是指在一组可能的组合上的等概率样本吗?
  • 嗨,Ferdinand @Ferdinand.kraft 1. 在任何给定的集合组合中,重叠的可能性比没有重叠的可能性大,但我很难告诉你实际的比例。 2. 当我将您的函数应用于一组 45 组可变长度和重叠时,R 停止响应。我什至无法单击强制退出的应用程序(我使用的是 Mac)。我最终不得不手动重新启动计算机。
  • 我对此有点好奇。您可以上传您的列表并发布链接吗?你确定你有一个 named 列表 numeric 向量吗?顺便说一句,我同时正在研究一种不同的方法:-)
  • 嗨,费迪南德,我已经修改了我原来的帖子。它现在包含创建虚拟数据集的代码,该数据集创建数字向量的命名列表。为了让它工作,你必须加载开头列出的系统发育包。每个数字向量是来自单系亚进化枝的物种名称的矢量,并且列表是大于 10 种和少于 40 种的所有单系亚进化枝。感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 2015-12-06
  • 2011-04-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-19
相关资源
最近更新 更多