【问题标题】:Improving performance of sapply function that simulates qbeta values提高模拟 qbeta 值的 sapply 函数的性能
【发布时间】:2016-01-11 15:24:29
【问题描述】:

我使用这个 sapply 函数:

set.seed(1)
data<-matrix(runif(1000000,0,1),1000000,2)

sapply(seq(0.0025, 0.9975, by=0.005), function (x) qbeta(x, data$a, data$b))

这可能需要很长时间,因为数据可能有 1 百万行。 a 和 b 是唯一的随机值。

如何提高性能?是从每一行查找参数需要时间,还是不可避免?我也尝试过并行版本,它缩短了时间,但速度仍然很慢。

一些结果(我在 38k 行上做了这个):

> system.time(matrix(qbeta(rep(seq(0.0025, 0.9975, by=0.005),each=nrow(data)),data$a, data$b),nrow=nrow(data)))
   user  system elapsed 
  34.53    0.00   34.53 
> system.time(sapply(seq(0.0025, 0.9975, by=0.005), function (x) qbeta(x, data$a, data$b)))
   user  system elapsed 
  34.22    0.00   34.21 

这是我的并行代码:

steps<-seq(0.0025, 0.9975, by=0.005)

qbeta.func <- function(x, data) {
  return(qbeta(x, data$a, data$b) * data$value)
}

cl <- makeCluster(rep("localhost",4), type = "SOCK")
t1 <- Sys.time()
data <- parSapply(cl, steps, qbeta.func, data)#
stopCluster(cl)
#data <- data[1:20,1:20]

【问题讨论】:

  • 很慢,因为你使用不正确。 qbeta 是矢量化的,因此您不需要使用 sapply 一次评估一个值;例如qbeta(seq(0.0025, 0.9975, by=0.005), .5, 1.5).
  • @nrussell 我想,他们希望每一百万行都有 200 个值(对于 p 点的网格)。
  • @Frank 啊,谢谢,这不清楚,因为 OP 没有提供可重现的示例
  • matrix(qbeta(rep(seq(0.0025, 0.9975, by=0.005),each=nrow(data)),data$a, data$b),nrow=nrow(data))
  • @nicola 请作为答案发布...为了更快的速度(如有必要),可能值得分块并行化。

标签: r performance sapply


【解决方案1】:

您可以在不使用 sapply 的情况下获得结果,因为 qbeta 是矢量化的。我们重复网格值nrow(df) 次。最后,您将获得一个matrix,其行是data 对应行的qbeta 的值。注意:考虑到大量的时间,这可能会很慢。不要认为你可以从这里大大加快速度,除非你并行化或使用更强大的 PC。试试看:

res<-matrix(qbeta(rep(seq(0.0025, 0.9975, by=0.005),
            each=nrow(data)),data$a, data$b),
            nrow=nrow(data))

编辑

这里我将做一个简单的并行示例。我们使用doParallel 包。我们将data data.frame 拆分为一个块列表,然后我们为每个块调用上面的行。从一开始:

#create the data (just 10000 rows)
set.seed(1)    
data<-as.data.frame(matrix(runif(10000,0,1),10000,2,dimnames=list(NULL,letters[1:2])))
#split in 10 1000 rows chunks
dataSplit<-split(data,(seq_len(nrow(data))-1)%/%1000)
#define the function to make the qbeta calculation
qbetaVec<-function(grid,values)
           matrix(qbeta(rep(grid,each=nrow(values)),values$a,values$b),nrow=nrow(values))
#define the grid
grid<-seq(0.0025, 0.9975, by=0.005)
#full calculation
system.time(res<-qbetaVec(grid,data))   
# user  system elapsed 
#5.103   0.007   5.115
#now we parallelize
library(doParallel)
#set the number of clusters
cl <- makeCluster(8)
registerDoParallel(cl)
#now the calculation with foreach and dopar
system.time(res2<-foreach(i=1:10) %dopar% qbetaVec(grid,dataSplit[[i]]))
#  user  system elapsed 
# 0.026   0.019   1.404
#now we put all together
res3<-do.call(rbind,res2)
identical(res3,res)
#[1] TRUE

【讨论】:

  • 你能告诉我如何分块运行它吗?我认为我的并行代码不够好@nicola
  • 嗨,我在 runif 中输入了 100,000,我还在行中输入了 100,000。相同(res3,res)虽然给出了错误。如果有提示,它还会关闭一些未使用的连接。我究竟做错了什么?无论如何,这太不可思议了。我认为它比我尝试的并行版本更快。你介意告诉我了解这些东西的一个好的起点是什么吗?我认为它很迷人。我学习数学,我很好奇它是如何工作的,尤其是 BLAS、MKL 库以及任何和所有优化技术之间的差异。非常感谢!
  • 我会使用doRNG 进行并行化以确保可重复性。
  • @Roland Tx 发表评论。不知道doRNG。但是,这里需要吗?据我所知,循环基本上只是调用qbeta 并没有生成随机数。
  • @nicola 对不起,你是对的。我应该更仔细地阅读。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-12
  • 1970-01-01
  • 2022-07-13
相关资源
最近更新 更多