これは、Rのトピックについてよく考えられています.SOの投稿hereとhereを参照してください。 for()
の代わりに*apply()
を使用すると、明快さが改善され、並列化が容易になり、場合によっては問題をスピードアップすることができます。しかし、おそらくあなたの本当のの質問は、「あなたが不幸になるのに十分な時間がかかるので、これをもっと速くするにはどうすればいいですか」という質問です。ループ内では、3つの異なるタスクを実行しています。
は、プロットを作る
filter()
- を使用してデータフレームのチャンクをブレイク
- 。
- プロットをjpegに保存します。
これら3つの手順をすべて実行する方法は複数ありますので、すべてを試してみましょう。私は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()
をプロファイリングすることはまだ(2.18秒に比べて1.96秒)、そのほとんどがprint(mcplt)
の時間だ費やし、それは以前よりもわずかに速いです。物事をスピードアップする1つの方法は、より小さいデバイス(解像度が低いか小さいイメージ)を使用することです。私がjpeg()
のデフォルトを使用した場合、その差はより大きく、もっと25%速くなりました。私もpng()
にデバイスを変更しようとしましたが、それは違いはありませんでした。
プロファイリングに基づいて、私はこれが役に立たないと思っていますが、完全性のために、forループを使い、dplyr内のすべてをdo()
で実行しようとします。ここでは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()
そのコードを実行すると、
Error: Results are not data frames at positions: 1, 2, 3
を与えるが、動作するようです。 print()メソッドがdata.frameを返さないため、do()
が返されたときにエラーが発生すると思います。それをプロファイリングすると、全体的に1.78秒という速さで少し速く走っているようです。しかし、問題を引き起こしていないとしても、エラーを生成するソリューションは嫌いです。
私はここでやらなくてはいけませんが、私はすでにどこに注意を払うべきかについて大いに学びました。しようとする他のものが含まれます:
- を別のプロセスでデータフレームの各チャンクを実行するために
parallel
または類似のものを使用します。私は問題がファイルを保存している場合に役立つかどうかはわかりませんが、画像のレンダリングがCPUによって行われた場合、それはそうだと思います。
- dplyrの代わりにdata.tableを試してください。ただし、やはり遅い印刷部分です。
- ggplot2の代わりにベースグラフィックスと格子グラフィックスをプロットしてみてください。私は相対的なスピードについては分かりませんが、それは変わる可能性があります。
- 高速なハードドライブを購入してください!私はちょうど私の家庭用コンピュータのf()と普通のハードドライブとを私の作業機械にSSDで比較しました - それは上記のタイミングより約3倍遅いです。
あなたのループは書かれているように素晴らしいと思います。ループはRで非常に不公平な悪評をたくさん受ける。 – bdemarest
ループはOKだと同意する。コードはtidyrをロードしますが、使用しません。ループの字下げが改善され、 'paste(...、sep =" ")'は 'paste0(...)'または 'sprintf'を使った方が良いでしょう。 –
一般的なループスタイル関数の多くは基底R関数として組み込まれているため、そのように使用する必要がありますが、それらは低レベル言語には存在しないため、別の言語の背景を持つプログラマーはRベクトル/配列の要素の合計を計算するような単純な場面でさえ、ループのために行くこと。もちろん、これはもっと複雑なケースに拡張することができます。しかし、私は 'for'ループを使って異なる変数のいくつかの(グラフィック的に)同一のプロットを生成することは、私自身が行うように、完全にうまく使用することに同意します。 – Molx