繁体   English   中英

在R上简化模拟

[英]Simplify Simulations on R

正如我在上一个问题中提到的那样。 我是编程新手,没有任何经验,但我很高兴能够学习。 但是,我遇到了以下问题,我的教授给了我们以下内容:

sim1 <- function(n) {
  xm <- matrix(nrow=n,ncol=2)
  for (i in 1:n) {
    d <- rnorm(1)
    if (runif(1) < 0.5) {
      xm[i,1] <- 1
      xm[i,2] <- 2.5*d + 69
    } else {
      xm[i,1] <- 0
      xm[i,2] <- 2*d + 64
    }
  }
  return(xm)
}

执行以下任务:尝试提高此代码的效率。 使用speed.test查看是否改进了生成n = 1000个观测值。

我终于至少能够弄清楚这段代码的作用了,尽管如此,我完全迷失了如何使这段代码更有效率。

任何帮助都意味着很多。 谢谢!

我会做我认为最明显的步骤,即将rnorm()移出循环并利用其矢量化特性(如粗略提及的那样)

sim2 <- function(n) {
    xm <- matrix(nrow=n, ncol=2)
    d <- rnorm(n)
    for (i in 1:n) {
        if (runif(1) < 0.5) {
            xm[i,1] <- 1
            xm[i,2] <- 2.5*d[i] + 69
        } else {
            xm[i,1] <- 0
            xm[i,2] <- 2*d[i] + 64
        }
    }
    return(xm)
}

n <- 1e3
set.seed(1); system.time(s1 <- sim1(n)); system.time(s2 <- sim2(n))
#  user  system elapsed 
# 0.019   0.004   0.023 
#  user  system elapsed 
# 0.010   0.000   0.009 

t.test(s1[,2], s2[,2]) # Not identical, but similar, again alluded to by rawr

只是这给了我们一个合理的改进。 使用runif()也可以做类似的事情,但我会留给你。

如果您需要一些阅读材料,我可以推荐Hadley Wickhams Advanced R和优化代码一章。

如果您想知道,确实可以消除循环和条件。

如果可能的话,不要在R. rep使用循环,并且rnorm将在一次调用中非常快速地填充具有5,10或500,000个值的向量。 调用rnorm(1) 500,000次是一种浪费,比简单地调用rnorm(500000)要慢得多。 这就像乘坐法拉利车道,1英尺并停下来,1英尺并停下来,一遍又一遍地到达目的地。

此函数将返回与您的函数统计相同的结果。 但是,它不是使用循环,而是以R方式执行操作。

sim2 <- function(n) {
    n1 <- floor(n/2)  #this is how many of the else clause we'll do
    n2 <- n - n1  #this is how many of the if clause we'll do
    col11 <- rep(0, n1) #bam! we have a vector filled with 0s
    col12 <- (rnorm(n1) * 2) + 64 #bam! vector filled with deviates
    col21 <- rep(1, n2) #bam! vector filled with 1s
    col22 <- (rnorm(n2) * 2.5) + 69 #bam! vector filled with deviates
    xm <- cbind(c(col11,col21), c(col12,col22)) #now we have a matrix, 2 cols, n rows
    return(xm[sample(nrow(xm)),]) #shuffle the rows, return matrix
}

没有循环! 功能可能很明显,但如果不是,我会解释。 首先, n1n2只是适当地分割n的大小(占奇数)。

接下来,可以消除每个元素的二项式过程(即if(runif(1) < 0.5) {} else {} ),因为我们知道在sim1 ,一半的矩阵属于if条件而一半属于else (见下面的证明)。 当我们知道它是50/50时,我们不需要为每个元素决定随机路径。 所以,我们将首先完成所有else 50%:我们用n / 2 0s( col11 )填充一个向量,用n / 2个随机偏差填充另一个向量( col11平均值= 0,sd = 1)使用结果向量col12偏离,乘以2并加64。 50%完成了。

接下来,我们完成第二个50%( if部分)。 我们用n / 2 1s( col21 )填充向量,用随机偏差填充另一个向量,并且对于每个偏差,乘以2.5并加69。

我们现在有4个向量,我们将变成一个矩阵。 步骤1:我们使用c函数将col11 (填充n / 2 0s)和col21 (填充n / 2 1s)粘合在一起以获得向量(n个元素)。 步骤2:使用ccol12col22在一起(用偏差填充)以获得载体(如1列xn行矩阵)。 注意:0s / 1s与基于64/69公式的正确偏差相关联。 步骤3:使用cbind从向量中生成矩阵( xm ):0/1向量变为第1列,偏向量变为第2列。步骤4:使用矩阵中的行数(应该只是nnrow 步骤5:使用sample随机排序所有行号,制作一个随机向量。 步骤6:根据混洗向量创建一个新的(未命名的)矩阵,按顺序放置xm的行。 步骤4-6的要点只是随机排序行,因为sim1的二项式过程会产生随机的行顺序。

此版本运行速度快866%!

> system.time({ sim1(500000)})
   user  system elapsed 
  1.341   0.179   1.527 
> system.time({ sim2(500000)})
   user  system elapsed 
  0.145   0.011   0.158 

如果您担心这可以保持二项式过程的完整性,请考虑二项式过程做两件事:1)它将1与2.5*d+69等式相关联,0与2*d + 64等式相关联 -由于行被完整地洗牌,因此保持了关联; 2)50%进入if子句,50%进入else子句,如下所示。

sim3 <- function(n) {
    a <- 0
    for(j in 1:n) {
        if(runif(1) < 0.5) {
            a <- a + 1
        }
    }
    return(a/n)
}
> sim3(50)
[1] 0.46
> sim3(5000)
[1] 0.4926
> sim3(10000)
[1] 0.5022
> sim3(5000000)
[1] 0.4997844

二项式过程产生50%1s和50%0s(第1列)。

我可以建议的一个优化是你创建矩阵,默认值为0 一旦创建了默认值为0矩阵,就不需要在函数中填充值0

修改后的代码如下所示:

sim1 <- function(n) {
#create matrix with 0 value. 
xm <- matrix(0,nrow=n,ncol=2) 
for (i in 1:n) {
d <- rnorm(1)
if (runif(1) < 0.5) {
 xm[i,1] <- 1
 xm[i,2] <- 2.5*d + 69
} else {
 #xm[i,1] <- 0    --- No longer needed
 xm[i,2] <- 2*d + 64
}
}
return(xm)
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM