我有一个矢量列表,比如vecs = list(vec1=1:3, vec2=1:5, vec3=2:6, vec4=1:7)
。我想删除所有列表成员,如果它包含在另一个列表成员中。例如,vecs$vec1
是vecs$vec2
,(或vecs$vec4
)的一部分,因此我想删除它。
我想要一个非常快速的实现,因为length(vecs)
非常大。我所做的是首先按长度vecs
对vecs = 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]
答案 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)
然后,我将数据转换为包含/不包含的0
和1
矩阵:
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%
和any
比setdiff
和length
更有效。
# 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循环和中断有时会更快,但你不能提前知道。
此方法适用于字符串或整数。