我的问题:需要从一组集中找到所有不相交(非重叠)的集合。
背景:我正在使用比较系统发育方法来研究鸟类的性状进化。我有一棵约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))
答案 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"
请注意,包含A
和B
的集合同时不会显示,因为它们的非空交集。此外,同时显示C
和D
的集合。其他人都可以。
注意:空集合character(0)
始终是有效组合。
创建所有可能的不相交集合后,您可以应用任何要继续的过滤器。
编辑:
我从第一个函数中删除了行if( length(L)==0 ) return(list(branch))
;它不需要。
性能:如果集合之间存在相当大的重叠,则该函数运行速度很快。例如:
set.seed(1)
L&lt; - lapply(1:50,函数(。)样本(x = 1200,大小= 20))
姓名(L)&lt; - c(字母,字母)[1:50]
system.time(DC&lt; - DisjointCollections(L))
结果:
# user system elapsed
# 9.91 0.00 9.92
找到的馆藏总数:
> length(DC)
[1] 121791