不同长度矢量的矢量化运算

时间:2013-01-26 01:39:01

标签: r

假设我有以下两个数组:

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循环?

3 个答案:

答案 0 :(得分:2)

您可以展开vt

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提供矢量化解决方案,并与pminpmax相结合,所有这些都是矢量化的。它有点扭曲/复杂的逻辑,但它值得付出所有的努力。

使用stepfun + pmin + pmax的优势:

  • 快速燃烧(见下面的基准测试)
  • 不受矩阵大小的限制(请参阅运行Jonathan代码时巨大矢量上的错误)

首先,这个想法受到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) + 1sorted 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次。