2013-08-21 14 views
19

ggplot2パッケージは、私がこれまでに作業した中で最も優れたプロットシステムですが、大規模なデータセット(〜50kポイント)ではパフォーマンスが実際には良いものではありません。私はShinyを使ってWeb解析を提供していますが、ggplot2をプロットバックエンドとして使用していますが、特にベースのグラフィックスとは対照的に、パフォーマンスにはあまり満足していません。私の質問は、このパフォーマンスを高める具体的な方法があるかどうかです。私は私のMacPro網膜上で次のタイミングを取得ggplot2パフォーマンスを向上させる

library(ggplot2) 

n = 86400 # a day in seconds 
dat = data.frame(id = 1:n, val = sort(runif(n))) 

dev.new() 

gg_base = ggplot(dat, aes(x = id, y = val)) 
gg_point = gg_base + geom_point() 
gg_line = gg_base + geom_line() 
gg_both = gg_base + geom_point() + geom_line() 

benchplot(gg_point) 
benchplot(gg_line) 
benchplot(gg_both) 
system.time(plot(dat)) 
system.time(plot(dat, type = 'l')) 

出発点は、次のコード例である

> benchplot(gg_point) 
     step user.self sys.self elapsed 
1 construct  0.000 0.000 0.000 
2  build  0.321 0.078 0.398 
3 render  0.271 0.088 0.359 
4  draw  2.013 0.018 2.218 
5  TOTAL  2.605 0.184 2.975 
> benchplot(gg_line) 
     step user.self sys.self elapsed 
1 construct  0.000 0.000 0.000 
2  build  0.330 0.073 0.403 
3 render  0.622 0.095 0.717 
4  draw  2.078 0.009 2.266 
5  TOTAL  3.030 0.177 3.386 
> benchplot(gg_both) 
     step user.self sys.self elapsed 
1 construct  0.000 0.000 0.000 
2  build  0.602 0.155 0.757 
3 render  0.866 0.186 1.051 
4  draw  4.020 0.030 4.238 
5  TOTAL  5.488 0.371 6.046 
> system.time(plot(dat)) 
    user system elapsed 
    1.133 0.004 1.138 
# Note that the timing below depended heavily on wether or net the graphics device 
# was in view or not. Not in view made performance much, much better. 
> system.time(plot(dat, type = 'l')) 
    user system elapsed 
    1.230 0.003 1.233 

私のセットアップにいくつかの詳細情報:

> sessionInfo() 
R version 2.15.3 (2013-03-01) 
Platform: x86_64-apple-darwin9.8.0/x86_64 (64-bit) 

locale: 
[1] C/UTF-8/C/C/C/C 

attached base packages: 
[1] stats  graphics grDevices utils  datasets methods base  

other attached packages: 
[1] ggplot2_0.9.3.1 

loaded via a namespace (and not attached): 
[1] MASS_7.3-23  RColorBrewer_1.0-5 colorspace_1.2-1 dichromat_2.0-0 
[5] digest_0.6.3  grid_2.15.3  gtable_0.1.2  labeling_0.1  
[9] munsell_0.4  plyr_1.8   proto_0.3-10  reshape2_1.2.2  
[13] scales_0.2.3  stringr_0.6.2  
+0

複数のコアにまたがってプロットを配布したり、必要に応じてキャッシュすることはできますか? – orizon

+0

プロットをスピードアップするものはすべて許容されますが、キャッシングは実際には解決されません。ユーザーが実際に描画する新しいプロット(軸の変更、ラインの色など)を必要とする状況です。 –

+3

ggplot2には組み込みのタイミングシステム 'benchplot()'があり、なぜそれが遅いのかを識別するのに役立ちます。 – baptiste

答えて

7

ハドリーは彼の新しいパッケージdplyrとについてはtalkが涼しかったはuser2013です。しかし、彼はおそらく自分自身についてもっとよく話すことができます。

私はあなたのアプリケーション設計がどのようなものかはわかりませんが、データをRに送る前にデータベース内の前処理を行うことがよくあります。たとえば、時系列をプロットする場合、実際には表示する必要はありませんX軸上の1日の1秒ごと。その代わりに、たとえば最小/最大/平均を集計して取得することができます。 1または5分の時間間隔。

私は数年前に書いた関数の例の下で、SQLのようなことをしました。この特定の例では、時間はエポックミリ秒として保存されたため、モジュロ演算子を使用しています。しかし、SQL内のデータがdate/datetime構造体として適切に格納されている場合、SQLには、より洗練されたネイティブメソッドがあり、期間別に集約します。

#' @param table name of the table 
#' @param start start time/date 
#' @param end end time/date 
#' @param aggregate one of "days", "hours", "mins" or "weeks" 
#' @param group grouping variable 
#' @param column name of the target column (y axis) 
#' @export 
minmaxdata <- function(table, start, end, aggregate=c("days", "hours", "mins", "weeks"), group=1, column){ 

    #dates 
    start <- round(unclass(as.POSIXct(start))*1000); 
    end <- round(unclass(as.POSIXct(end))*1000); 

    #must aggregate 
    aggregate <- match.arg(aggregate); 

    #calcluate modulus 
    mod <- switch(aggregate, 
    "mins" = 1000*60, 
    "hours" = 1000*60*60, 
    "days" = 1000*60*60*24, 
    "weeks" = 1000*60*60*24*7, 
    stop("invalid aggregate value") 
); 

    #we need to add the time differene between gmt and pst to make modulo work 
    delta <- 1000 * 60 * 60 * (24 - unclass(as.POSIXct(format(Sys.time(), tz="GMT")) - Sys.time())); 

    #form query 
    query <- paste("SELECT", group, "AS grouping, AVG(", column, ") AS yavg, MAX(", column, ") AS ymax, MIN(", column, ") AS ymin, ((CMilliseconds_g +", delta, ") DIV", mod, ") AS timediv FROM", table, "WHERE CMilliseconds_g BETWEEN", start, "AND", end, "GROUP BY", group, ", timediv;") 
    mydata <- getquery(query); 

    #data 
    mydata$time <- structure(mod*mydata[["timediv"]]/1000 - delta/1000, class=c("POSIXct", "POSIXt")); 
    mydata$grouping <- as.factor(mydata$grouping) 

    #round timestamps 
    if(aggregate %in% c("mins", "hours")){ 
    mydata$time <- round(mydata$time, aggregate) 
    } else { 
    mydata$time <- as.Date(mydata$time); 
    } 

    #return 
    return(mydata) 
} 
+1

+1!私は集約が良い選択であることに同意します。これは確かに探検する価値があります。しかし、クライアント(科学者)が純粋にパフォーマンスのためのこのようなスムージングに満足するかどうかはわかりません。 –

+1

パフォーマンスだけではありません。目は軸上の86400点を読み取ることができず、モニターには表示する解像度がありません。ビッグ(ish)データを使用したい場合は、常に集計を行う必要があります。そうしないと、プロットが混乱することになります。 – Jeroen

+1

私は同意しますが、この例では1行しか描いていません。 〜100kポイントがいくつかのファセットに分散され、スムージングが追加されたとしましょう。このようにして、たくさんのデータを引き出す必要がある良いプロットを簡単に得ることができます。 –

関連する問題