2013-02-21 179 views
3

我使用"%within[]%" <- function(x,y){x>=y[1] & x<=y[2]}(意思是x在紧凑集合y)在R代码很多,但我非常确定这是非常缓慢。你有更快的东西吗?它需要为定义>的所有内容工作。最快的R实现在

编辑:x可以是载体和y升序2个elments矢量...

EDIT2:奇怪的是,没有人(据我所知)写了一包rOperator实现快速C运营商如%w/i[]%, %w/i[[%, ...

编辑3:我意识到我的问题太笼统了,因为在x,y上做出的假设会修改任何结果,我想我们应该关闭它,谢谢您的意见。

+1

你的意思是'y'总是一个二元向量,'y [1] juba 2013-02-21 12:53:14

+1

函数(...){return(...)}'=>'函数(...)...' – 2013-02-21 12:56:57

+0

您可能需要'&'而不是'&&'。 – Roland 2013-02-21 12:59:53

回答

6
"%within[]%" <- function(x,y){x>=y[1] & x<=y[2]} 

x <- 1:10 
y <- c(3,5) 

x %within[]% y 
"%within[]2%" <- function(x,y) findInterval(x,y,rightmost.closed=TRUE)==1 
x %within[]2% y 

library(microbenchmark) 

microbenchmark(x %within[]% y,x %within[]2% y) 

Unit: microseconds 
      expr min lq median uq max 
1 x %within[]% y 1.849 2.465 2.6185 2.773 11.395 
2 x %within[]2% y 4.928 5.544 5.8520 6.160 37.265 

x <- 1:1e6 
microbenchmark(x %within[]% y,x %within[]2% y) 

Unit: milliseconds 
      expr  min  lq median  uq  max 
1 x %within[]% y 27.81535 29.60647 31.25193 56.68517 88.16961 
2 x %within[]2% y 20.75496 23.07100 24.37369 43.15691 69.62122 

这可能是Rcpp的工作。

+0

+1 TIL如何正确使用'microbenchmark'。 – juba 2013-02-21 13:21:12

1

好了,我不知道这是否可以考虑缓慢或没有,但这里是一个有点基准:

R> within <- function(x,y){return(x>=y[1] & x<=y[2])} 
R> microbenchmark(within(2,c(1,5))) 
Unit: microseconds 
       expr min  lq median uq max neval 
within(2, c(1, 5)) 2.667 2.8305 2.9045 2.969 15.818 100 

R> within2 <- function(x,y) x>=y[1] & x<=y[2] 
R> microbenchmark(within2(2,c(1,5))) 
Unit: microseconds 
       expr min  lq median uq max neval 
within2(2, c(1, 5)) 2.266 2.3205 2.398 2.483 12.472 100 

R> microbenchmark(2>=1 & 2<=5) 
Unit: nanoseconds 
      expr min lq median uq max neval 
2 >= 1 & 2 <= 5 781 821.5 850 911 5701 100 

如此看来,省略return,由康拉德·鲁道夫的建议,速度有点事情。但不写函数要快得多。

+1

我的建议实际上并没有像样式那么多(在这里''return'函数调用只是多余的)。但是,对于这些结果并不感到惊讶。 – 2013-02-21 13:05:51

+1

&&只比较向量的第一要素:1:4 < 4 && 1:4 > 2提供虚假和不(FALSE,FALSE,TRUE,FALSE) – 2013-02-21 13:08:49

+0

@JanvanderLaan是的,我知道,但如果你不想矢量操作,'&& '有点快。 – juba 2013-02-21 13:10:39

1

如果x包含许多值,则基于树的结构可提供更好的性能。如果您可以将您的要求限制为数值,则有2个选项

可以在Bioconductor软件包IRanges中找到整数间隔树的实现。

默认情况下,RSQLite正在编译启用rtrees的嵌入式SQLite库。这可以用于任何数值。

+0

我意识到,这是比我想象的,理想的,应与数字(所以一切都像日期,POSIXct ......),而且字符(带字典顺序)的工作更加复杂。 – statquant 2013-02-21 13:10:09

+0

将日期转换为整数(自纪元起的毫秒数)是微不足道的(您的假名表明您没有使用历史或历史之前的日期)。字符串并不特别,如何做到这一点需要你做出设计决定(前缀匹配?后缀匹配?严格相同的长度?) – lgautier 2013-02-21 13:18:23

+0

是的,不确定是否需要转换,因为POSIXct和Date存储为double(奇怪的是Date)内部。理解字符串... – statquant 2013-02-21 13:25:53

3

你可以用一个简单的RCPP实现一个小的性能改进:

library(Rcpp) 
library(microbenchmark) 

withinR <- function(x,y) x >= y[1] & x <= y[2] 
cppFunction("LogicalVector withinCpp(const NumericVector& x, const NumericVector& y) { 
    double min = y[0], max = y[1]; 

    int n = x.size(); 
    LogicalVector out(n); 

    for(int i = 0; i < n; ++i) { 
    double val = x[i]; 
    if (NumericVector::is_na(val)) { 
     out[i] = NA_LOGICAL; 
    } else { 
     out[i] = val >= min & val <= max; 
    } 

    } 
    return out; 
}") 

x <- sample(100, 1e5, rep = T) 

stopifnot(all.equal(withinR(x, c(25, 50)), withinCpp(x, c(25, 50)))) 

microbenchmark(
    withinR(x, c(25, 50)), 
    withinCpp(x, c(25, 50)) 
) 

C++版本是4倍左右我的电脑上更快。如果你想要使用更多的Rcpp技巧,你可以进一步调整它,但这看起来已经很快了。即使是R版本也需要在可能出现瓶颈之前非常频繁地调用。

# Unit: microseconds 
#      expr min lq median uq max 
# 1 withinCpp(x, c(25, 50)) 635 659 678 1012 27385 
# 2 withinR(x, c(25, 50)) 1969 2031 2573 2954 4082