假设我有两个值向量:
a <- c(1,3,4,5,6,7,3)
b <- c(3,5,1,3,2)
我想对FUN
的每个输入应用一些函数a
,而不是整个b
,这是最有效的方法。
更具体地说,在这种情况下,对于a
中的每个元素,我想知道“a”的每个值,b
中有多少元素大于或等于值。天真的做法是做到以下几点:
sum(a < b)
当然,这不起作用,因为它试图并行迭代每个向量并给出警告:
较长的物体长度不是较短物体长度的倍数
该命令的输出btw为3
。
然而,在我的情况下,我希望看到的是输出:
0 2 4 4 5 5 2
当然,我意识到我可以使用for循环这样做:
out <- c()
for (i in a) {
for (i in a) { out[length(out) + 1] = sum(b<i)}
}
同样,我可以使用sapply
:
sapply(a, function(x)sum(b<x))
然而,我正在努力成为一名优秀的R程序员并远离for循环而sapply
似乎非常缓慢。还有其他选择吗?
对于它的价值,我这样做了几百万次,length(b)
总是小于length(a)
而length(a)
的范围是1到30。
答案 0 :(得分:4)
试试这个:
findInterval(a - 0.5, sort(b))
通过使用更简单的sort
包装来避免findInterval
和b)避免order
和.Internal
的开销,从而提高速度:
order2 = function(x) .Internal(order(T, F, x))
findInterval2 = function(x, vec, rightmost.closed=F, all.inside=F) {
nx <- length(x)
index <- integer(nx)
.C('find_interv_vec', xt=as.double(vec), n=length(vec),
x=as.double(x), nx=nx, as.logical(rightmost.closed),
as.logical(all.inside), index, DUP = FALSE, NAOK=T,
PACKAGE='base')
index
}
> system.time(for (i in 1:10000) findInterval(a - 0.5, sort(b)))
user system elapsed
1.22 0.00 1.22
> system.time(for (i in 1:10000) sapply(a, function(x)sum(b<x)))
user system elapsed
0.79 0.00 0.78
> system.time(for (i in 1:10000) rowSums(outer(a, b, ">")))
user system elapsed
0.72 0.00 0.72
> system.time(for (i in 1:10000) findInterval(a - 0.5, b[order(b)]))
user system elapsed
0.42 0.00 0.42
> system.time(for (i in 1:10000) findInterval2(a - 0.5, b[order2(b)]))
user system elapsed
0.16 0.00 0.15
定义findInterval2
和order2
的复杂性可能仅在你有大量N的迭代时才有必要。
更大N的时间安排:
> a = rep(a, 100)
> b = rep(b, 100)
> system.time(for (i in 1:100) findInterval(a - 0.5, sort(b)))
user system elapsed
0.01 0.00 0.02
> system.time(for (i in 1:100) sapply(a, function(x)sum(b<x)))
user system elapsed
0.67 0.00 0.68
> system.time(for (i in 1:100) rowSums(outer(a, b, ">")))
user system elapsed
3.67 0.26 3.94
> system.time(for (i in 1:100) findInterval(a - 0.5, b[order(b)]))
user system elapsed
0 0 0
> system.time(for (i in 1:100) findInterval2(a - 0.5, b[order2(b)]))
user system elapsed
0 0 0
答案 1 :(得分:3)
一种选择是使用outer()
将二元运算符函数>
应用于a
和b
:
> outer(a, b, ">")
[,1] [,2] [,3] [,4] [,5]
[1,] FALSE FALSE FALSE FALSE FALSE
[2,] FALSE FALSE TRUE FALSE TRUE
[3,] TRUE FALSE TRUE TRUE TRUE
[4,] TRUE FALSE TRUE TRUE TRUE
[5,] TRUE TRUE TRUE TRUE TRUE
[6,] TRUE TRUE TRUE TRUE TRUE
[7,] FALSE FALSE TRUE FALSE TRUE
Q的答案由上面结果的行和给出:
> rowSums(outer(a, b, ">"))
[1] 0 2 4 4 5 5 2
对于此示例数据集,此解决方案比findIntervals()
略快但不是很多:
> system.time(replicate(1000, findInterval(a - 0.5, sort(b))))
user system elapsed
0.131 0.000 0.132
> system.time(replicate(1000, rowSums(outer(a, b, ">"))))
user system elapsed
0.078 0.000 0.079
它也比sapply()
版本略快,但略有增加:
> system.time(replicate(1000, sapply(a, function(x)sum(b<x))))
user system elapsed
0.082 0.000 0.082
@Charles指出findInterval()
示例中的大部分时间都由sort()
使用,可以通过order()
来规避。完成此操作后,findInterval()
解决方案比outer()
解决方案更快:
> system.time(replicate(1000, findInterval(a - 0.5, b[order(b)])))
user system elapsed
0.049 0.000 0.049
答案 2 :(得分:0)
只是一个附加说明:如果您知道每个向量的值的范围,那么首先计算最大值和分钟可能会更快,例如
order2 = function(x) .Internal(order(T, F, x))
findInterval2 = function(x, vec, rightmost.closed=F, all.inside=F) {
nx <- length(x)
index <- integer(nx)
.C('find_interv_vec', xt=as.double(vec), n=length(vec),
x=as.double(x), nx=nx, as.logical(rightmost.closed),
as.logical(all.inside), index, DUP = FALSE, NAOK=T,
PACKAGE='base')
index
}
f <- function(a, b) {
# set up vars
a.length <- length(a)
b.length <- length(b)
b.sorted <- b[order2(b)]
b.min <- b.sorted[1]
b.max <- b.sorted[b.length]
results <- integer(a.length)
# pre-process minimums
v.min <- which(a <= b.min)
# pre-process maximums
v.max <- which(a > b.max)
results[v.max] <- b.max
# compare the rest
ind <- c(v.min, v.max)
results[-ind] <- findInterval2(a[-ind] - 0.5, b.sorted)
results
}
其中给出以下时间
> N <- 10
> n <- 1e5
> b <- runif(n, 0, 100)
> a <- runif(n, 40, 60) # NB smaller range of values than b
> summary( replicate(N, system.time(findInterval2(a - 0.5, b[order2(b)]))[3]) )
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0300 0.0300 0.0400 0.0390 0.0475 0.0500
> summary( replicate(N, system.time(f(a, b))[3]) )
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.010 0.030 0.030 0.027 0.030 0.040
但是,如果你不提前知道范围,或者无法对它们进行有根据的猜测,那么这可能会更慢。
答案 3 :(得分:0)
我非常警惕在生产代码中使用R的内部。内部可以在不同版本之间轻松更改。
sort.int比sort更快 - 而且b [order(b)]比sort.int(b)更快,这简直很奇怪。 R肯定能改善它的分类...
除非你使用R的内部,否则使用vapply似乎更快:
> system.time(for (i in 1:10000) findInterval(a - 0.5, sort(b)))
user system elapsed
0.99 0.00 0.98
> system.time(for (i in 1:10000) findInterval(a - 0.5, sort.int(b)))
user system elapsed
0.8 0.0 0.8
> system.time(for (i in 1:10000) findInterval(a - 0.5, b[order(b)]))
user system elapsed
0.32 0.00 0.32
> system.time(for (i in 1:10000) sapply(a, function(x)sum(b<x)))
user system elapsed
0.61 0.00 0.59
> system.time(for (i in 1:10000) vapply(a, function(x)sum(b<x), 0L))
user system elapsed
0.18 0.00 0.19