快速检查一个向量是否是另一个向量的一部分

时间:2014-12-04 14:40:26

标签: r vector

我有一个矢量列表,比如vecs = list(vec1=1:3, vec2=1:5, vec3=2:6, vec4=1:7)。我想删除所有列表成员,如果它包含在另一个列表成员中。例如,vecs$vec1vecs$vec2,(或vecs$vec4)的一部分,因此我想删除它。

我想要一个非常快速的实现,因为length(vecs)非常大。我所做的是首先按长度vecsvecs = vecs[ order(unlist(lapply(vecs, length))) ]成员进行排序,然后对check_member = vecs[i]进行排序,检查它是否属于vecs[ i+1 ], vecs[ i+2 ]...。是否有更好的策略?完整代码:

vecs = list(vec1=1:3, vec2=1:5, vec3=2:6, vec4=1:7, vec5=2:3)
vecs = vecs[ order(unlist(lapply(vecs, length))) ] ##sort by member length
vecs_len = length(vecs)
toRemove = numeric(vecs_len ) ##record whether to remove this member
for( i in 1:(vecs_len-1 ))
for( j in (i+1):(vecs_len ))
{
   if( length( setdiff(vecs[[i]],vecs[[j]]) )==0  ) {toRemove[i] = 1; break} ##check whether vecs[[i]] is part of vecs[[j]]

}
vecs = vecs[!toRemove]

3 个答案:

答案 0 :(得分:4)

请在大数据上试试这个。当你在评论中澄清时,我从字符向量开始。我也假设向量不包含重复项(如果有的话,很容易通过lapply(vecs, unique)修复)

vecs <- list(vec1=letters[1:3],
             vec2=letters[1:5],
             vec3=letters[2:6],
             vec4=letters[1:7])

首先,将您的数据转换为共享相同级别的因子列表(即整数):

vlevels <- unique(unlist(vecs))
nlev    <- length(vlevels)
fvecs   <- lapply(vecs, factor, levels = vlevels)

然后,我将数据转换为包含/不包含的01矩阵:

vtabs <- vapply(fvecs, tabulate, integer(nlev), nbins = nlev)
#      vec1 vec2 vec3 vec4
# [1,]    1    1    0    1
# [2,]    1    1    1    1
# [3,]    1    1    1    1
# [4,]    0    1    1    1
# [5,]    0    1    1    1
# [6,]    0    0    1    1
# [7,]    0    0    0    1

接下来,我计算交叉产品以查看两个向量共有多少个元素,并与每个向量的长度进行比较,以确定第一个向量是否是第二个向量的子集。

num.match <- crossprod(vtabs)
vlen <- sapply(vecs, length)
is.subset.mat <- num.match == vlen
diag(is.subset.mat) <- FALSE
#       vec1  vec2  vec3  vec4
# vec1 FALSE  TRUE FALSE  TRUE
# vec2 FALSE FALSE FALSE  TRUE
# vec3 FALSE FALSE FALSE  TRUE
# vec4 FALSE FALSE FALSE FALSE

最后,我按行顺序判断每个向量是否是任何其他向量的子集:

is.subset <- rowSums(is.subset.mat) > 0L
# vec1  vec2  vec3  vec4 
# TRUE  TRUE  TRUE FALSE 

你只剩下过滤:

out <- vecs[!is.subset]

如果您的初始列表太大而且速度太慢,那么我建议您逐块运行它,然后再对结果进行操作。

答案 1 :(得分:1)

在这里做了一些基准测试后,我们可以看到你可以稍微修改一下原始代码,以获得更好的加速。使用%in%anysetdifflength更有效。

# Your code
fun1 <- function(vecs){
  vecs_len = length(vecs)
  toRemove = numeric(vecs_len ) ##record whether to remove this member
  for( i in 1:(vecs_len-1 ))
    for( j in (i+1):(vecs_len ))
    {
      if( length( setdiff(vecs[[i]],vecs[[j]]) )==0  ) toRemove[i] = 1 ##check whether vecs[[i]] is part of vecs[[j]]

    }
  vecs = vecs[!toRemove]
  return(vecs)
}

# flodel's code
fun2 <- function(vecs){
  vlevels <- unique(unlist(vecs))
  nlev    <- length(vlevels)
  fvecs   <- lapply(vecs, factor, levels = vlevels)
  vtabs <- vapply(fvecs, tabulate, integer(nlev), nbins = nlev)
  num.match <- crossprod(vtabs)
  vlen <- sapply(vecs, length)
  is.subset.mat <- num.match == vlen
  diag(is.subset.mat) <- FALSE
  is.subset <- rowSums(is.subset.mat) > 0L
  out <- vecs[!is.subset]
  return(out)  
}

# slight modification
fun3 <- function(vecs){
  vecs_len = length(vecs)
  toRemove = numeric(vecs_len ) ##record whether to remove this member
  for( i in 1:(vecs_len-1 ))
    for( j in (i+1):(vecs_len ))
    {
      if( any(vecs[[i]] %in% vecs[[j]])  ) toRemove[i] = 1 ##check whether vecs[[i]] is part of vecs[[j]]

    }
  vecs = vecs[!toRemove]
  return(vecs)
}

microbenchmark(fun1(vecs), fun2(vecs), fun3(vecs), times=100L)
Unit: microseconds
       expr     min       lq      mean   median       uq     max neval
 fun1(vecs) 154.919 166.4245 179.62297 172.5880 180.3950 356.681   100
 fun2(vecs) 220.255 231.5555 279.99874 237.7185 290.9335 609.398   100
 fun3(vecs)  50.544  54.0370  64.86082  57.3250  64.5155 291.345   100

答案 2 :(得分:0)

我认为以下内容应该有效。它将为您提供这些矢量的列表,这些矢量是其他一个的子集。它将是它自身的一个子集 - 所以我只对那些它是至少两个向量的子集(即它自己和另一个)的那些做。归功于https://stat.ethz.ch/pipermail/r-help/2005-March/068491.html%in in%function。

dfSubsets <- list()

ds <- lapply(vecs,function(x){
        subsets = 0
        lapply( vecs, function(y){
            if(all(x %in% y)){subsets <<- subsets + 1}
        })
        if(subsets > 1){
            dfSubsets <<- c(dfSubsets,list(x))
        }
}
)

dfSubsets

如果你想要一个子集的矢量索引列表,请告诉我。

我确信dplyr也可能有所帮助,但我对此并不熟悉。

应用通常可以提高效率 - 当然,如果你只关心它是否是一个向量的子集,使用for循环和中断有时会更快,但你不能提前知道。

此方法适用于字符串或整数。