【问题标题】:How to make a loop run faster in R?如何使循环在 R 中运行得更快?
【发布时间】:2012-02-09 18:47:41
【问题描述】:

我想使用arms() 每次获取一个样本,并在我的函数中创建一个类似以下的循环。它运行得很慢。我怎样才能让它跑得更快?谢谢。

library(HI)    
dmat <- matrix(0, nrow=100,ncol=30)
system.time(
    for (d in 1:100){
        for (j in 1:30){
            y <- rep(0, 101)
            for (i in 2:100){

                y[i] <- arms(0.3, function(x) (3.5+0.000001*d*j*y[i-1])*log(x)-x,
                    function(x) (x>1e-4)*(x<20), 1)       
            }
        dmat[d, j] <- sum(y)
        }
    }
) 

【问题讨论】:

  • 您有三个嵌套的 for 循环。你基本上是在 O(n^3) 中运行。你真的需要它们吗?
  • @simchona 是的。我都需要。
  • 尝试stackoverflow.com/a/8474941/636656 获取一些建议
  • 看起来你这里的代码只是运行相同的语句 100*30*100 次,每次都覆盖 y。请用显示您正在尝试做什么的代码澄清您的问题。
  • y &lt;- seq(0, 1:100) 应该做什么?与y &lt;- 0:1 相同。也许你打算y &lt;- numeric(100) ??

标签: r loops


【解决方案1】:

这是一个基于 Tommy 回答的版本,但避免了所有循环:

library(multicore) # or library(parallel) in 2.14.x
set.seed(42)
m = 100
n = 30
system.time({
    arms.C <- getNativeSymbolInfo("arms")$address
    bounds <- 0.3 + convex.bounds(0.3, dir = 1, function(x) (x>1e-4)*(x<20))
    if (diff(bounds) < 1e-07) stop("pointless!")
    # create the vector of z values
    zval <- 0.00001 * rep(seq.int(n), m) * rep(seq.int(m), each = n)
    # apply the inner function to each grid point and return the matrix
    dmat <- matrix(unlist(mclapply(zval, function(z)
            sum(unlist(lapply(seq.int(100), function(i)
                .Call(arms.C, bounds, function(x) (3.5 + z * i) * log(x) - x,
                      0.3, 1L, parent.frame())
            )))
        )), m, byrow=TRUE)
}) 

在多核机器上,这将非常快,因为它将负载分散到内核之间。在单核机器上(或对于较差的 Windows 用户),您可以将上面的 mclapply 替换为 lapply 并且与 Tommy 的答案相比仅获得轻微的加速。但请注意,并行版本的结果会有所不同,因为它将使用不同的 RNG 序列。

请注意,任何需要评估 R 函数的 C 代码本质上都会很慢(因为解释代码很慢)。我添加了 arms.C 只是为了消除所有 R->C 开销以使 moli 高兴;),但这没有任何区别。

您可以通过使用列优先处理再挤出几毫秒(问题代码是行优先的,需要重新复制,因为 R 矩阵始终是列优先的)。

编辑:我注意到自从 Tommy 回答后 moli 稍微改变了这个问题 - 所以你必须使用循环而不是 sum(...) 部分,因为 y[i] 是依赖的,所以 function(z)看起来像

function(z) { y <- 0
    for (i in seq.int(99))
         y <- y + .Call(arms.C, bounds, function(x) (3.5 + z * y) * log(x) - x,
                        0.3, 1L, parent.frame())
    y }

【讨论】:

  • 不——这不是因为对所有值的调用都是独立的。它可以实现为循环,但不是必须的(如果您阅读上述内容,这就是重点)。
【解决方案2】:

嗯,一种有效的方法是消除 arms 内部的开销。它会进行一些检查并每次调用indFunc,即使您的结果始终相同。 一些其他的评估也可以在循环之外进行。这些优化将我机器上的时间从 54 秒缩短到 6.3 秒左右。 ...答案是相同的。

set.seed(42)
#dmat2 <- ##RUN ORIGINAL CODE HERE##

# Now try this:
set.seed(42)
dmat <- matrix(0, nrow=100,ncol=30)
system.time({
    e <- new.env()
    bounds <- 0.3 + convex.bounds(0.3, dir = 1, function(x) (x>1e-4)*(x<20))
    f <- function(x) (3.5+z*i)*log(x)-x
    if (diff(bounds) < 1e-07) stop("pointless!")
    for (d in seq_len(nrow(dmat))) {
        for (j in seq_len(ncol(dmat))) {
            y <- 0
            z <- 0.00001*d*j
            for (i in 1:100) {
                y <- y + .Call("arms", bounds, f, 0.3, 1L, e)
            }
            dmat[d, j] <- y
        }
    }
}) 

all.equal(dmat, dmat2) # TRUE

【讨论】:

  • 感谢您的帮助。我的 logdens 非常复杂,将使用 arm() 中的每个样本点进行更新。所以运行需要很长时间。也许我需要回去写c函数。
  • 所以你必须根据 arm 的样本更改 logdens?如果是这样,那么问题肯定需要重写
【解决方案3】:

为什么不喜欢这样?

dat <- expand.grid(d=1:10, j=1:3, i=1:10)

arms.func <- function(vec) {
  require(HI)
  dji <- vec[1]*vec[2]*vec[3]
  arms.out <- arms(0.3, 
                   function(x,params) (3.5 + 0.00001*params)*log(x) - x,
                   function(x,params) (x>1e-4)*(x<20),
                   n.sample=1,
                   params=dji)

  return(arms.out)
}

dat$arms <- apply(dat,1,arms.func)

library(plyr)
out <- ddply(dat,.(d,j),summarise, arms=sum(arms))

matrix(out$arms,nrow=length(unique(out$d)),ncol=length(unique(out$j)))

但是,它仍然是单核且耗时。但这不是 R 慢,而是 arm 函数。

【讨论】:

  • system.time(y &lt;- arms(runif(1,1e-4,20), function(x) (3.5)*log(x)-x, function(x) (x&gt;1e-4)*(x&lt;20), 100*30*100)) user system elapsed 2.739 0.010 2.766 所以我猜 R 和 c 之间的通信花费了太多时间。
猜你喜欢
  • 2021-07-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多