3

我有以下排序向量:

> 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 内。解决方案应该假设向量是排序的,以避免检查每个元素。

当我们想要将向量中的项目用作另一个对象的索引时,这种解决方案可以在非常大的向量的向量化操作中派上用场。对于一个应用程序,请参阅此线程

4

4 回答 4

10

要按索引在向量中包含元素:

v [2:10]

排除某些元素

v [-c (1, 11) ]

只包括一定范围:

v <- v [v>=1 & v <=10]

如果允许我假设,就像在您的示例中一样,要修剪的元素数 << 向量中的元素数,那么我认为我可以击败二进制搜索:

> 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
于 2013-04-25T16:56:59.647 回答
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))
于 2013-04-25T16:57:42.863 回答
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

它有点笨拙,并且有一些讨论认为二进制搜索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
于 2013-04-25T18:55:36.283 回答
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
于 2013-04-25T16:58:01.953 回答