-->

更有效的策略,其中()或匹配()(More efficient strategy for which

2019-09-01 03:09发布

我有正数和负数的矢量

vec<-c(seq(-100,-1), rep(0,20), seq(1,100))

所述载体是比例大,并且呈现出随机的一组值。 我不得不反复找负数的向量的数量......我发现这是非常低效的。

因为我只需要找到负数的数量,以及矢量进行排序,我只需要知道第一个0或正数的指数(有可能是在实际的随机向量没有0)。

目前我使用这个代码,以查找长度

length(which(vec<0))

但是这迫使R键完成整个载体,但由于它的排序,也没有必要。

我可以用

match(0, vec)

但我的矢量并不总是有0

所以我的问题是,是否有某种匹配()函数的适用条件,而不是寻找一个特定的值吗? 或者是有经营我这()的代码更有效的方法?

谢谢

Answer 1:

提供了到目前为止所有的解决方案表示可以创建一个logical(length(vec))做这个完全或部分扫描。 当你注意,向量进行排序。 我们可以做一个二进制搜索利用这一点。 我开始思考我是超级聪明和落实这在C甚至更高的速度,但与调试算法的索引麻烦(这是棘手的部分!)。 所以我写了它在R:

f3 <- function(x) {
    imin <- 1L
    imax <- length(x)
    while (imax >= imin) {
        imid <- as.integer(imin + (imax - imin) / 2)
        if (x[imid] >= 0)
            imax <- imid - 1L
        else
            imin <- imid + 1L
    }
    imax
}

为了与其他建议比较

f0 <- function(v) length(which(v < 0))
f1 <- function(v) sum(v < 0)
f2 <- function(v) which.min(v < 0) - 1L

和乐趣

library(compiler)
f3.c <- cmpfun(f3)

导致

> vec <- c(seq(-100,-1,length.out=1e6), rep(0,20), seq(1,100,length.out=1e6))
> identical(f0(vec), f1(vec))
[1] TRUE
> identical(f0(vec), f2(vec))
[1] TRUE
> identical(f0(vec), f3(vec))
[1] TRUE
> identical(f0(vec), f3.c(vec))
[1] TRUE
> microbenchmark(f0(vec), f1(vec), f2(vec), f3(vec), f3.c(vec))
Unit: microseconds
      expr       min        lq     median         uq       max neval
   f0(vec) 15274.275 15347.870 15406.1430 15605.8470 19890.903   100
   f1(vec) 15513.807 15575.229 15651.2970 17064.8830 18326.293   100
   f2(vec) 21473.814 21558.989 21679.3210 22733.1710 27435.889   100
   f3(vec)    51.715    56.050    75.4495    78.5295   100.730   100
 f3.c(vec)    11.612    17.147    28.5570    31.3160    49.781   100

也许有,我已经错了一些棘手的边界情况! 移动到C,我做

library(inline)
f4 <- cfunction(c(x = "numeric"), "
    int imin = 0, imax = Rf_length(x) - 1, imid;
    while (imax >= imin) {
        imid = imin + (imax - imin) / 2;
        if (REAL(x)[imid] >= 0)
            imax = imid - 1;
        else
            imin = imid + 1;
    }
    return ScalarInteger(imax + 1);
")

> identical(f3(vec), f4(vec))
[1] TRUE
> microbenchmark(f3(vec), f3.c(vec), f4(vec))
Unit: nanoseconds
      expr   min      lq  median      uq   max neval
   f3(vec) 52096 53192.0 54918.5 55539.0 69491   100
 f3.c(vec) 10924 12233.5 12869.0 13410.0 20038   100
   f4(vec)   553   796.0   893.5  1004.5  2908   100

findInterval上前当有人问一个类似的问题R-帮助列表。 它是缓慢的,但安全检查vec实际上是分类和处理NA值。 如果想住上边缘(可以说是不差,实施F3或F4),然后

f5.i <- function(v)
    .Internal(findInterval(v, 0 - .Machine$double.neg.eps, FALSE, FALSE))

几乎是一样快的C实现,但可能更强大和矢量(即查找值向量在第二个参数,便于范围样计算)。



Answer 2:

使用sum()和逻辑比较:

sum( vec < 0 )
[1] 100

这将是相当快,当你总结的逻辑, TRUE为1, FALSE为0,因此总的将是负值的数量。

嗯哦,我觉得一个基准比较的需要... :-)向量的长度为2E5

library(microbenchmark)
vec<-c(seq(-100,-1,length.out=1e5), rep(0,20), seq(1,100,length.out=1e5))
microbenchmark( (which.min(vec < 0) - 1L) , (sum( vec < 0 )) )

Unit: milliseconds
                      expr      min       lq   median       uq       max neval
 (which.min(vec < 0) - 1L) 1.883847 2.130746 2.554725 3.141787 75.943911   100
            (sum(vec < 0)) 1.398100 1.500639 1.508688 1.745088  2.662164   100


Answer 3:

你可以使用which.min

 which.min(vec < 0) - 1L

这将返回第一个FALSE值,即第一个0。



文章来源: More efficient strategy for which() or match()