在编码时,我经常只是编码它在我脑海中的表现。虽然我认为我从一开始就学习了有效的R编码(例如,避免for
... if
循环),但我的解决方案并不总是真正受性能驱动。不幸的是,有时知道什么是最有效的代码至关重要 - 我想学习它!
目前我正在模拟组合成列表的多个数据帧。在模拟之后,我需要第二个数据帧,其中包含整个列表中所有列的均值和SD。 ('Simulation'在这里意味着某些变量正在从其他数据帧进行模拟/重采样,其他变量只是随机正常或具有特定b_0的二项分布值。为了简洁起见,我省去了第一部分重新采样这里。)
我的代码(参见下面的例子)完美地产生了预期的结果,但它似乎是第一个,有点慢(我说的是真实的几个小时),第二个,高度RAM消耗(为此我临时减少了列表中模拟dfs的数量。)
对于模拟我知道在函数中定义data.frame可能是一个问题,但我不知道如何更好地做到这一点。对于均值/ SD数据帧,我只能说它甚至更慢。
如何提高代码的性能?任何人都可能另外提供一些关于这种性能提升的基本规则(或相关信息来源)吗?
(我正在使用R 3.x / 64和Win 7/64 AMD FX(tm)-8350八核处理器,4 GHz,16 GB机器。运行时CPU保持相当酷,RAM呻吟声极限。)
这里我给出一个示例代码,其中包含评论中的测量系统时间:
# definitions
r <- 1e5 # number of rows
n <- 1e3 # number of dfs
# simulation of the list
library(dplyr)
system.time(list <- lapply(1:n, function(i){ # 59.05 sec
data.frame(a = rbinom(r, 1, .375)) %>%
mutate(
b = rnorm(r, 0, 2),
c = .42 * rnorm(r, 0, 6),
d = rbinom(r, 11, c(1:11)/11),
e = rbinom(r, 1, .1),
f = .02 * rnorm(r, 0, 5))
}))
# df w/ means & sds
system.time(list.s <- data.frame( # 73.20 sec
list.mean = round(rowMeans(sapply(list, colMeans)), 2),
list.sd = round(sapply(do.call(rbind, list), sd), 2)))
答案 0 :(得分:1)
扩展Rolands评论,您可以预先创建大量人口数据,然后为每个“样本”/迭代简单地对其进行子集化。例如:
## create large population data:
s <- 1e6 # probably big enough for this problem
set.seed(12)
d <- matrix(NA, nrow = s, ncol = 6) #..
# using matrix is more efficient than data.frame
d[,1] <- rbinom(s, 1, .375)
d[,2] <- rnorm(s, 0, 2)
d[,3] <- .42 * rnorm(s, 0, 6)
d[,4] <- rbinom(s, 11, c(1:11)/11)
d[,5] <- rbinom(s, 1, .1)
d[,6] <- .02 * rnorm(s, 0, 5)
head(d)
# [,1] [,2] [,3] [,4] [,5] [,6]
# [1,] 0 0.73853351 1.097805 1 0 -0.06233008
# [2,] 1 -0.05311206 4.447807 2 0 -0.01117972
# [3,] 1 1.71576276 -3.619708 6 0 0.02962562
# [4,] 0 1.92188205 -1.062585 2 0 0.03195146
# [5,] 0 -1.41097404 1.706067 2 0 -0.07751285
# [6,] 0 4.19130890 2.663374 8 0 -0.02316172
r <- 1e4 # number of rows
n <- 1e2 # number of dfs
si <- replicate(n, sample.int(s, r)) # get indexes for each sample
# loop trougth samples and subset data:
nSamples <- lapply(1:n, function(x) {
d[si[, x],]
})
# and calculate colMeans:
list.mean2 = round(rowMeans(sapply(nSamples, colMeans)), 3)
list.mean2
# [1] 0.376 0.000 -0.003 5.999 0.100 0.000
与您的结果进行比较:
require(dplyr)
list1 <- lapply(1:n, function(i){
data.frame(a = rbinom(r, 1, .375)) %>%
mutate(
b = rnorm(r, 0, 2),
c = .42 * rnorm(r, 0, 6),
d = rbinom(r, 11, c(1:11)/11),
e = rbinom(r, 1, .1),
f = .02 * rnorm(r, 0, 5))
})
list.mean1 = round(rowMeans(sapply(list1, colMeans)), 3)
list.mean1
# a b c d e f
# 0.375 -0.002 0.004 6.001 0.100 0.000
我们可以看到平均值的估计与这个小的n值非常相似。
P.S。因为'list'是基本R函数,所以不应该用该名称命名变量!
让我们将两种方法都包含在测试时间的函数中:
mySim <- function(s, r, n) {
d <- matrix(NA, nrow = s, ncol = 6)
d[,1] <- rbinom(s, 1, .375)
d[,2] <- rnorm(s, 0, 2)
d[,3] <- .42 * rnorm(s, 0, 6)
d[,4] <- rbinom(s, 11, c(1:11)/11)
d[,5] <- rbinom(s, 1, .1)
d[,6] <- .02 * rnorm(s, 0, 5)
si <- lapply(1:n, function(x) sample.int(s, r))
nSamples <- lapply(si, function(x) {
d[x,]
})
list.mean2 = rowMeans(sapply(nSamples, colMeans))
list.mean2
}
yourSim <- function(r, n) {
require(dplyr)
list1 <- lapply(1:n, function(i){
data.frame(a = rbinom(r, 1, .375)) %>%
mutate(
b = rnorm(r, 0, 2),
c = .42 * rnorm(r, 0, 6),
d = rbinom(r, 11, c(1:11)/11),
e = rbinom(r, 1, .1),
f = .02 * rnorm(r, 0, 5))
})
list.mean1 = rowMeans(sapply(list1, colMeans))
list.mean1
}
system.time(mySim(1e6, 1e4, 1e2)) # ~ 0.6 sek
system.time(yourSim(1e4, 1e2)) # ~ 1.5 sek
# if s = 1e7 :
system.time(mySim(1e7, 1e4, 1e2)) # ~ 4.53 sek
我们可以看到,为小n和r值创建大量人口数据并不会提高速度。
让我们将s
作为1e6(100万),但你应该自己调查一下
已经足够了。
如果我们为更大的'r'和'n'值主持时间安排:
system.time(r1 <- mySim(1e6, 1e5, 1e3)) # ~ 20 sek
system.time(r2 <- yourSim(1e5, 1e3)) # ~ 60 sek
round(r1, 3)
# [1] 0.376 -0.003 -0.002 6.001 0.100 0.00
round(r2, 3)
# a b c d e f
# 0.375 0.000 0.000 6.000 0.100 0.000
关于计算SD: 也许你想在'matrixStats'包中使用'rowSds()'或'colSds()'?
另外我建议您调查Rcpp包,这对于加速代码更有用。