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次观测值。
我终于至少能够弄清楚这段代码的作用,但是,我完全迷失了如何使这段代码更有效率。
任何帮助都意味着很多。 谢谢!
答案 0 :(得分:2)
我会做我认为最明显的步骤,即将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和章Optimising code。
如果您想知道,确实可以消除循环和条件。
答案 1 :(得分:2)
如果可能,请不要在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
}
没有循环!功能可能很明显,但如果不是,我会解释。首先,n1
&amp; n2
只是适当地分割n
的大小(占奇数)。
接下来,可以消除每个元素的二项式过程(即if(runif(1) < 0.5) {} else {}
),因为我们知道在sim1
中,矩阵的一半落入if
条件而一半落入else
(见下面的证明)。当我们知道它是50/50时,我们不需要为每个元素决定 一遍又一遍地随机路径。所以,我们首先要做所有else
50%:我们用n / 2 0s(col11
)填充一个向量,用n / 2个随机偏差填充另一个向量(均值= 0,默认情况下,sd = 1),对于每个偏差,乘以2并加64,结果向量为col12
。那50%已经完成了。
接下来,我们完成第二个50%(if
部分)。我们用n / 2 1s(col21
)填充向量,用随机偏差填充另一个向量,并且对于每个偏差,乘以2.5并加69.
我们现在有4个向量,我们将变成一个矩阵。步骤1:我们使用col11
函数将col21
(填充n / 2 0s)和c
(填充n / 2 1s)粘合在一起以获得向量(n个元素)。第2步:使用col12
将col22
和c
粘合在一起(填充偏差)以获得向量(如1列x n行矩阵)。注意:0s / 1s与基于64/69公式的正确偏差相关联。步骤3:使用cbind
从向量中生成矩阵(xm
):0/1向量变为第1列,偏向量变为第2列。步骤4:获取矩阵中的行数(使用n
进行nrow
)。步骤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
等式相关联1}} equation - 由于行被完整地洗牌,因此保持了关联; 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列)。
答案 2 :(得分:0)
我可以建议的一个优化是您创建默认值为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)
}