这是一个经过深思熟虑的 R 主题,请参阅 SO 帖子 here 和 here。 Answers to this question 强调 *apply() 替代 for() 提高了清晰度,使并行化更容易,并在某些情况下加速问题。但是,大概您的真正问题是“我如何更快地做到这一点”,因为它花费的时间足够长,以至于您不满意。在您的循环中,您正在执行 3 个不同的任务。
- 使用
filter() 拆分数据帧的一部分
- 画一个情节。
- 将绘图保存为 jpeg。
有多种方法可以完成所有这三个步骤,因此让我们尝试评估所有这些步骤。我将使用来自 ggplot2 的菱形数据,因为它比汽车数据大。我希望通过这种方式,方法之间的性能差异会很明显。我从this chapter of Hadley Wickham's book on measuring performance学到了很多东西。
为了可以使用分析,我将以下代码块放入函数中,并将其保存在名为 for_solution.r 的单独 R 文件中。
f <- function(){
param <- unique(diamonds$cut)
for (i in param){
mcplt <- diamonds %>% filter(cut==i) %>% ggplot(aes(x=carat, y=price)) +
geom_point() +
facet_wrap(~color) +
ggtitle(paste("Cut: ",i,sep=""))
ggsave(mcplt, file=paste("Cut",i,".jpeg",sep=""))
}
}
然后我做:
library(dplyr)
library(ggplot2)
source("for_solution.r",keep.source=TRUE)
Rprof(line=TRUE)
f()
Rprof(NULL)
summaryRprof(lines="show")
检查该输出,我发现代码块花费了 97.25% 的时间只是保存文件。检查ggsave() 的源代码我可以看到该函数正在执行大量防御性编程来识别输出类型,然后打开图形设备,打印,然后关闭设备。所以我想知道手动执行该步骤是否会有所帮助。我还将利用 jpeg 设备会自动为每个页面生成新文件,只打开和关闭设备一次这一事实。
f1 <- function(){
param <- unique(diamonds$cut)
jpeg("cut%03d.jpg",width=par("din")[1],height=par("din")[2],units="in",res=300) # open the jpeg device, change defaults to match ggsave()
for (i in param){
mcplt <- diamonds %>% filter(cut==i) %>% ggplot(aes(x=carat, y=price)) +
geom_point() +
facet_wrap(~color) +
ggtitle(paste("Cut: ",i,sep=""))
print(mcplt)
}
dev.off()
}
现在再次进行分析
Rprof(line=TRUE)
f1()
Rprof(NULL)
summaryRprof(lines="show")
f1() 仍然将大部分时间花在print(mcplt) 上,并且比以前稍快(1.96 秒比 2.18 秒)。加快速度的一种可能方法是使用更小的设备(分辨率更低或图像更小);当我使用jpeg() 的默认值时,差异更大,快了 25%。我还尝试将设备更改为png(),但这并没有什么不同。
根据分析,我不希望这会有所帮助,但为了完整起见,我将尝试取消 for 循环并使用 do() 在 dplyr 中运行所有内容。我发现 this question 和 this one 在这里很有帮助。
jpeg("cut%03d.jpg",width=par("din")[1],height=par("din")[2],units="in",res=300) # open the jpeg device, change defaults to match ggsave()
plots = diamonds %>% group_by(cut) %>%
do({plot=ggplot(aes(x=carat, y=price),data=.) +
geom_point() +
facet_wrap(~color) +
ggtitle(paste("Cut: ",.$cut,sep=""))
print(plot)})
dev.off()
运行该代码给出
错误:结果不是位置的数据帧:1、2、3
但它似乎工作。我相信当 do() 返回时会出现错误,因为 print() 方法没有返回 data.frame。分析它似乎表明它运行得更快一些,总共 1.78 秒。但我不喜欢产生错误的解决方案,即使它们不会引起问题。
我不得不在这里停下来,但我已经了解了很多关于将注意力集中在哪里的知识。其他可以尝试的方法包括:
- 使用
parallel 或类似的东西在单独的进程中运行数据帧的每个块。如果问题是保存文件,我不确定这会有所帮助,但如果渲染图像是由 CPU 完成的,我认为。
- 尝试使用 data.table 代替 dplyr,但同样是打印部分很慢。
- 尝试使用基本图形和点阵图形和 plotly 代替 ggplot2。我不知道相对速度,但它可能会有所不同。
- 购买更快的硬盘!我只是比较了我的家用电脑上的 f() 和普通硬盘驱动器的速度和我的工作机器上的 SSD ——它比上面的时间慢了大约 3 倍。