假设我有以下两个数组:
R <- 101
v <- array(0, dim <- c(R,2))
v[,1] <-runif(101)
t <- array(runif(5), dim <- c(5,2))
我想做的是为v的第二列中的每个单元分配以下函数的结果:
which.min(abs(v[r,1] - t[,1]))
因此,对于v的第二列中的每个单元格,我将有1,2,3,4或5.我知道我可以使用for循环遍历v的所有行r,但有人知道向量化这个操作的方法,这样我就不必诉诸(相当慢)for循环?
答案 0 :(得分:2)
您可以展开v
和t
:
V <- matrix(rep.int(v[,1],dim(t)[[1]]),ncol=dim(t)[[1]])
TT <- matrix(rep.int(t[,1],dim(v)[[1]]),ncol=dim(t)[[1]],byrow=T)
然后减去并获取每列的最大值:
max.col(-abs(V-TT))
答案 1 :(得分:2)
尽管名称不是真正的矢量化,但Vectorize
调用lapply
。但这给出了结果:
> Vectorize(function(r) which.min(abs(v[r,1] - t[,1])))(seq(nrow(v)))
## [1] 4 3 3 2 5 5 2 5 2 5 3 3 2 5 1 4 5 5 4 3 3 5 5 2 4 2 2 4 4 3 2 4 5 2
## [35] 2 3 2 4 4 1 5 5 2 3 2 4 5 5 3 5 2 4 4 2 4 5 5 5 5 5 4 3 3 5 5 3 2 3
## [69] 5 3 5 3 3 5 4 5 5 3 1 2 5 5 2 3 3 4 3 3 4 5 4 2 2 3 4 2 5 5 5 5 2
然后可以将此值分配给v[,2
]。
答案 2 :(得分:1)
我认为可以使用stepfun
提供矢量化解决方案,并与pmin
和pmax
相结合,所有这些都是矢量化的。它有点扭曲/复杂的逻辑,但它值得付出所有的努力。
使用stepfun
+ pmin
+ pmax
的优势:
首先,这个想法受到Jonathan Chang's
帖子here的启发。这里的小变化是你需要索引而不是差异。此外,我假设所有值都是正面(来自您的runif
输入)。您可以将此扩展到具有负输入的向量,但如果需要,我会将该任务留给您。在我进入代码和基准测试之前,让我解释一下stepfun
背后的想法。
假设您有两个向量x
(相当于v[,1]
)和y
(相当于t[,1]
)。现在,让我们对y
进行排序,并以这种方式在stepfun
上创建sorted y
:
y_sort <- sort(y)
step <- stepfun(y_sort, 0:length(y))
这有助于我们究竟如何?查询step(a)
会为y_sort
中的< a
提供最大值的索引。这可能需要一段时间才能收入。换句话说,值a
位于step(a)
中step(a) + 1
和sorted y (y_sort)
之间的位置。现在,我们要弄清楚的第一件事是,这两个值中的哪一个最接近a
。这是通过提取索引step(a)
和step(a)+1
以及y_sort
中与这些索引相对应的值并询问abs(a-y_sort[step(a)]) > abs(a - y_sort[step(a)+1])
来实现的。如果它是假的,那么step(a)
是你的索引,反之亦然。其次,从y
y_sort
取回原始索引,这可以通过在index.return = TRUE
中使用选项sort
获取相应的排序索引来实现。
我同意以这种方式遵循可能会非常复杂。但是检查代码并逐步运行它并使用上面的文本跟随它(如果需要)。最好的部分是a
可以是一个向量,所以它非常快!现在转到代码。
# vectorised solution using stepfun
vectorise_fun1 <- function(x, y) {
y_sort <- sort(abs(y), index.return = TRUE)
y_sval <- y_sort$x
y_sidx <- y_sort$ix
# stepfun
step_fun <- stepfun(y_sval, 0:length(y))
ix1 <- pmax(1, step_fun(x))
ix2 <- pmin(length(y), 1 + ix1)
iy <- abs(x - y_sval[ix1]) > abs(x - y_sval[ix2])
# construct output
res <- rep(0, length(x))
res[iy] <- y_sidx[ix2[iy]]
res[!iy] <- y_sidx[ix1[!iy]]
res
}
# obtaining result
out_arun <- vectorise_fun1(v[,1], t[,1])
# (or) v[,2] <- vectorise_fun1(v[,1], t[,1])
# Are the results identical?
# Matthew's solution
vectorise_fun2 <- function(x, y) {
res <- Vectorize(function(r) which.min(abs(x[r] - y)))(seq(length(x)))
}
out_matthew <- vectorise_fun2(v[,1], t[,1])
# Jonathan's solution
vectorise_fun3 <- function(x, y) {
V <- matrix(rep.int(x, length(y)), ncol = length(y))
TT <- matrix(rep.int(y, length(x)), ncol = length(y), byrow = T)
max.col(-abs(V-TT))
}
out_jonathan <- vectorise_fun3(v[,1], t[,1])
# Are the results identical?
> all(out_arun == out_matthew)
[1] TRUE
> all(out_arun == out_jonathan)
[1] TRUE
那么,有什么意义呢?所有结果都是相同的,stepfun
的函数非常庞大且难以理解。让我们采取一个巨大的向量。
x <- runif(1e4)
y <- runif(1e3)
现在,让我们的基准来看看优势:
require(rbenchmark)
> benchmark( out_arun <- vectorise_fun1(x,y),
out_matthew <- vectorise_fun2(x,y),
out_jonathan <- vectorise_fun3(x,y),
replications=1, order = "elapsed")
# test replications elapsed relative user.self
# 1 out_arun <- vectorise_fun1(x, y) 1 0.004 1.00 0.005
# 2 out_matthew <- vectorise_fun2(x, y) 1 0.221 55.25 0.169
# 3 out_jonathan <- vectorise_fun3(x, y) 1 1.381 345.25 0.873
# Are the results identical?
> all(out_arun == out_matthew)
[1] TRUE
> all(out_arun == out_jonathan)
[1] TRUE
因此,使用step_fun
的速度最快为55次,最多为345次!现在,让我们选择更大的载体。
x <- runif(1e5)
y <- runif(1e4)
require(rbenchmark)
> benchmark( out_arun <- vectorise_fun1(x,y),
out_matthew <- vectorise_fun2(x,y),
replications=1, order = "elapsed")
# test replications elapsed relative user.self
# 1 out_arun <- vectorise_fun1(x, y) 1 0.052 1.000 0.043
# 2 out_matthew <- vectorise_fun2(x, y) 1 16.668 320.538 11.849
Jonathan的功能导致分配错误:
Error in rep.int(x, length(y)) :
cannot allocate vector of length 1000000000
这里加速是320次。