我有以下排序的矢量:
> v
[1] -1 0 1 2 4 5 2 3 4 5 7 8 5 6 7 8 10 11
如何使用用户循环或隐式使用语言关键字删除-1,0和11条而不循环遍历整个向量?也就是说,我想修剪每个边缘处的向量,并且仅在每个边缘处修剪向量,使得排序的序列在我的min,max参数1和10内。解决方案应该假设向量已经排序避免检查每个元素。
当我们想要使用向量中的项作为另一个对象中的索引时,这种解决方案在非常大的向量的向量化操作中可以派上用场。对于一个应用程序,请参阅this thread。
答案 0 :(得分:10)
通过索引在向量中包含元素:
v [2:10]
排除某些元素
v [-c (1, 11) ]
仅包含一定范围:
v <- v [v>=1 & v <=10]
如果我允许这样做,就像在你的例子中那样,要修剪的元素的数量&lt;&lt;向量中的元素数量,那么我认为我可以击败二进制搜索:
> n<-1e8
> v<--3:(n+3)
>
> min <- 1
> max <- length(v)
>
> calcMin <- function(v, minVal){
+ while(v[min] < minVal){
+ min <- min + 1
+ }
+ min
+ }
>
> calcMax <- function(v, maxVal){
+ while(v[max] > maxVal){
+ max <- max - 1
+ }
+ max
+ }
>
> #Compute the min and max indices and create a sequence
> system.time(a <- v[calcMin(v, 1):calcMax(v,n)])
user system elapsed
1.030 0.269 1.298
>
> #do a binary search to find the elements (as suggested by @nograpes)
> system.time(b <- v[do.call(seq,as.list(findInterval(c(1,n),v)))])
user system elapsed
2.208 0.631 2.842
>
> #use negative indexing to remove elements
> system.time(c <- v[-c(1:(calcMin(v, 1)-1), (calcMax(v,n)+1):length(v))])
user system elapsed
1.449 0.256 1.704
>
> #use head and tail to trim the vector
> system.time(d <- tail(head(v, n=(calcMax(v,n)-length(v))), n=-calcMin(v, 1)+1))
user system elapsed
2.994 0.877 3.871
>
> identical(a, b)
[1] TRUE
> identical(a, c)
[1] TRUE
> identical(a, d)
[1] TRUE
答案 1 :(得分:5)
有很多方法可以做到,这里有一些:
> v <- -1:11 # creating your vector
> v[v %in% 1:10]
[1] 1 2 3 4 5 6 7 8 9 10
> setdiff(v, c(-1,0,11))
[1] 1 2 3 4 5 6 7 8 9 10
> intersect(v, 1:10)
[1] 1 2 3 4 5 6 7 8 9 10
还有两个选项,不是那么优雅。
> na.omit(match(v, 1:10))
> na.exclude(match(v, 1:10))
答案 2 :(得分:4)
所有先前的解决方案都隐式检查向量的每个元素。正如@Robert Kubrick指出的那样,这并没有利用向量已经排序的事实。
要利用向量的排序特性,您可以使用二进制搜索(通过findInterval
)查找开始和结束索引,而无需查看每个元素:
n<-1e9
v<--3:(n+3)
system.time(a <- v [v>=1 & v <=n]) # 68 s
system.time(b <- v[do.call(seq,as.list(findInterval(c(1,n),v)))]) # 15s
identical(a,b) # TRUE
这有点笨拙,有some discussion findInterval
中的二分搜索可能不完全有效,但总体思路就在那里。
正如评论中指出的,上述仅在索引位于向量中时才有效。这是一个我认为可行的功能:
in.range <- function(x, lo = -Inf, hi = +Inf) {
lo.idx <- findInterval(lo, x, all.inside = TRUE)
hi.idx <- findInterval(hi, x)
lo.idx <- lo.idx + x[lo.idx] >= lo
x[seq(lo.idx, hi.idx)]
}
system.time(b <- in.range(v, 1, n) # 15s
答案 3 :(得分:2)
您也可以使用%in%
:
vv <- c(-1, 0 ,1 ,2 ,4 ,5, 2 ,3 ,4, 5, 7 ,8, 5, 6, 7, 8, 10, 11)
vv[vv %in% 1:10]
[1] 1 2 4 5 2 3 4 5 7 8 5 6 7 8 10