R中是否有一个函数可以有效地检查一个值是否大于一个且小于另一个数字?它也应该与矢量一起使用。
基本上,我正在寻找以下功能的更快版本:
> in.interval <- function(x, lo, hi) (x > lo & x < hi)
> in.interval(c(2,4,6), 3, 5)
[1] FALSE TRUE FALSE
这里的问题是x
必须被触摸两次,并且与更有效的方法相比,计算消耗两倍的内存。在内部,我会假设它像这样工作:
tmp1 <- (x > lo)
tmp2 <- (x < hi)
retval <- tmp1 & tmp2
现在,在步骤2之后,两个布尔向量在内存中,x
必须被查看两次。我的问题是:是否有一个(内置?)函数可以一步完成所有这些操作,而无需分配额外的内存?
跟进此问题:R: Select values from data table in range
编辑:我已根据CauchyDistributedRV在https://gist.github.com/4344844的回答设置了一个Gist
答案 0 :(得分:6)
正如@James在评论中所说,诀窍是从x中减去低和高之间的中间值,然后检查该差异是否小于低和高之间距离的一半。或者,在代码中:
in.interval2 <- function(x, lo, hi) {
abs(x-(hi+lo)/2) < (hi-lo)/2
}
这与.bincode
hack一样快,并且是您正在寻找的算法的实现。您可以将其转换为C或C ++,并尝试加速。
与其他解决方案的比较:
x <- runif(1e6,1,10)
require(rbenchmark)
benchmark(
in.interval(x, 3, 5),
in.interval2(x, 3, 5),
findInterval(x, c(3, 5)) == 1,
!is.na(.bincode(x, c(3, 5))),
order='relative',
columns=c("test", "replications", "elapsed", "relative")
)
给出
test replications elapsed relative
4 !is.na(.bincode(x, c(3, 5))) 100 1.88 1.000
2 in.interval2(x, 3, 5) 100 1.95 1.037
3 findInterval(x, c(3, 5)) == 1 100 3.42 1.819
1 in.interval(x, 3, 5) 100 3.54 1.883
答案 1 :(得分:5)
findInterval
比in.interval
快。
library(microbenchmark)
set.seed(123L)
x <- runif(1e6, 1, 10)
in.interval <- function(x, lo, hi) (x > lo & x < hi)
microbenchmark(
findInterval(x, c(3, 5)) == 1L,
in.interval(x, 3, 5),
times=100)
与
Unit: milliseconds
expr min lq median uq max
1 findInterval(x, c(3, 5)) == 1L 23.40665 25.13308 25.17272 25.25361 27.04032
2 in.interval(x, 3, 5) 42.91647 45.51040 45.60424 45.75144 46.38389
如果不需要== 1L
则更快,如果找到的'间隔'超过1则有用
> system.time(findInterval(x, 0:10))
user system elapsed
3.644 0.112 3.763
如果速度至关重要,那么这个C实现虽然不能容忍,例如整数而不是数字参数,但速度很快
library(inline)
in.interval_c <- cfunction(c(x="numeric", lo="numeric", hi="numeric"),
' int len = Rf_length(x);
double lower = REAL(lo)[0], upper = REAL(hi)[0],
*xp = REAL(x);
SEXP out = PROTECT(NEW_LOGICAL(len));
int *outp = LOGICAL(out);
for (int i = 0; i < len; ++i)
outp[i] = (xp[i] - lower) * (xp[i] - upper) <= 0;
UNPROTECT(1);
return out;')
其他答案中提出的某些解决方案的时间是
microbenchmark(
findInterval(x, c(3, 5)) == 1L,
in.interval.abs(x, 3, 5),
in.interval(x, 3, 5),
in.interval_c(x, 3, 5),
!is.na(.bincode(x, c(3, 5))),
times=100)
与
Unit: milliseconds
expr min lq median uq
1 findInterval(x, c(3, 5)) == 1L 23.419117 23.495943 23.556524 23.670907
2 in.interval.abs(x, 3, 5) 12.018486 12.056290 12.093279 12.161213
3 in.interval_c(x, 3, 5) 1.619649 1.641119 1.651007 1.679531
4 in.interval(x, 3, 5) 42.946318 43.050058 43.171480 43.407930
5 !is.na(.bincode(x, c(3, 5))) 15.421340 15.468946 15.520298 15.600758
max
1 26.360845
2 13.178126
3 2.785939
4 46.187129
5 18.558425
在bin.cpp文件中重新审视速度问题
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
SEXP bin1(SEXP x, SEXP lo, SEXP hi)
{
const int len = Rf_length(x);
const double lower = REAL(lo)[0], upper = REAL(hi)[0];
SEXP out = PROTECT(Rf_allocVector(LGLSXP, len));
double *xp = REAL(x);
int *outp = LOGICAL(out);
for (int i = 0; i < len; ++i)
outp[i] = (xp[i] - lower) * (xp[i] - upper) <= 0;
UNPROTECT(1);
return out;
}
// [[Rcpp::export]]
LogicalVector bin2(NumericVector x, NumericVector lo, NumericVector hi)
{
NumericVector xx(x);
double lower = as<double>(lo);
double upper = as<double>(hi);
LogicalVector out(x);
for( int i=0; i < out.size(); i++ )
out[i] = ( (xx[i]-lower) * (xx[i]-upper) ) <= 0;
return out;
}
// [[Rcpp::export]]
LogicalVector bin3(NumericVector x, const double lower, const double upper)
{
const int len = x.size();
LogicalVector out(len);
for (int i=0; i < len; i++)
out[i] = ( (x[i]-lower) * (x[i]-upper) ) <= 0;
return out;
}
有时间
> library(Rcpp)
> sourceCpp("bin.cpp")
> microbenchmark(bin1(x, 3, 5), bin2(x, 3, 5), bin3(x, 3, 5),
+ in.interval_c(x, 3, 5), times=1000)
Unit: milliseconds
expr min lq median uq max
1 bin1(x, 3, 5) 1.546703 2.668171 2.785255 2.839225 144.9574
2 bin2(x, 3, 5) 12.547456 13.583808 13.674477 13.792773 155.6594
3 bin3(x, 3, 5) 2.238139 3.318293 3.357271 3.540876 144.1249
4 in.interval_c(x, 3, 5) 1.545139 2.654809 2.767784 2.822722 143.7500
使用常量len
而不是out.size()
作为循环绑定来获得大约相等的部分加速,并分配逻辑向量而不初始化它(LogicalVector(len)
,因为它将在循环中初始化。
答案 2 :(得分:4)
我能找到的主要加速是通过字节编译功能。即使是Rcpp解决方案(虽然使用Rcpp糖,而不是更深入的C解决方案)比编译解决方案慢。
library( compiler )
library( microbenchmark )
library( inline )
in.interval <- function(x, lo, hi) (x > lo & x < hi)
in.interval2 <- cmpfun( in.interval )
in.interval3 <- function(x, lo, hi) {
sapply( x, function(xx) {
xx > lo && xx < hi }
)
}
in.interval4 <- cmpfun( in.interval3 )
in.interval5 <- rcpp( signature(x="numeric", lo="numeric", hi="numeric"), '
NumericVector xx(x);
double lower = Rcpp::as<double>(lo);
double upper = Rcpp::as<double>(hi);
return Rcpp::wrap( xx > lower & xx < upper );
')
x <- c(2, 4, 6)
lo <- 3
hi <- 5
microbenchmark(
in.interval(x, lo, hi),
in.interval2(x, lo, hi),
in.interval3(x, lo, hi),
in.interval4(x, lo, hi),
in.interval5(x, lo, hi)
)
给了我
Unit: microseconds
expr min lq median uq max
1 in.interval(x, lo, hi) 1.575 2.0785 2.5025 2.6560 7.490
2 in.interval2(x, lo, hi) 1.035 1.4230 1.6800 2.0705 11.246
3 in.interval3(x, lo, hi) 25.439 26.2320 26.7350 27.2250 77.541
4 in.interval4(x, lo, hi) 22.479 23.3920 23.8395 24.3725 33.770
5 in.interval5(x, lo, hi) 1.425 1.8740 2.2980 2.5565 21.598
<小时/> 编辑:根据其他评论,这是一个更快的Rcpp解决方案,使用给定绝对值的技巧:
library( compiler )
library( inline )
library( microbenchmark )
in.interval.oldRcpp <- rcpp(
signature(x="numeric", lo="numeric", hi="numeric"), '
NumericVector xx(x);
double lower = Rcpp::as<double>(lo);
double upper = Rcpp::as<double>(hi);
return Rcpp::wrap( (xx > lower) & (xx < upper) );
')
in.interval.abs <- rcpp(
signature(x="numeric", lo="numeric", hi="numeric"), '
NumericVector xx(x);
double lower = as<double>(lo);
double upper = as<double>(hi);
LogicalVector out(x);
for( int i=0; i < out.size(); i++ ) {
out[i] = ( (xx[i]-lower) * (xx[i]-upper) ) <= 0;
}
return wrap(out);
')
in.interval.abs.sugar <- rcpp(
signature( x="numeric", lo="numeric", hi="numeric"), '
NumericVector xx(x);
double lower = as<double>(lo);
double upper = as<double>(hi);
return wrap( ((xx-lower) * (xx-upper)) <= 0 );
')
x <- runif(1E5)
lo <- 0.5
hi <- 1
microbenchmark(
in.interval.oldRcpp(x, lo, hi),
in.interval.abs(x, lo, hi),
in.interval.abs.sugar(x, lo, hi)
)
all.equal( in.interval.oldRcpp(x, lo, hi), in.interval.abs(x, lo, hi) )
all.equal( in.interval.oldRcpp(x, lo, hi), in.interval.abs.sugar(x, lo, hi) )
给了我
1 in.interval.abs(x, lo, hi) 662.732 666.4855 669.939 690.6585 1580.707
2 in.interval.abs.sugar(x, lo, hi) 722.789 726.0920 728.795 742.6085 1671.093
3 in.interval.oldRcpp(x, lo, hi) 1870.784 1876.4890 1892.854 1935.0445 2859.025
> all.equal( in.interval.oldRcpp(x, lo, hi), in.interval.abs(x, lo, hi) )
[1] TRUE
> all.equal( in.interval.oldRcpp(x, lo, hi), in.interval.abs.sugar(x, lo, hi) )
[1] TRUE
答案 3 :(得分:4)
如果您可以处理NA
,则可以使用.bincode
:
.bincode(c(2,4,6), c(3, 5))
[1] NA 1 NA
library(microbenchmark)
set.seed(42)
x = runif(1e8, 1, 10)
microbenchmark(in.interval(x, 3, 5),
findInterval(x, c(3, 5)),
.bincode(x, c(3, 5)),
times=5)
Unit: milliseconds
expr min lq median uq max
1 .bincode(x, c(3, 5)) 930.4842 934.3594 955.9276 1002.857 1047.348
2 findInterval(x, c(3, 5)) 1438.4620 1445.7131 1472.4287 1481.380 1551.419
3 in.interval(x, 3, 5) 2977.8460 3046.7720 3075.8381 3182.013 3288.020