2016-03-11 2 views
9

私は、一般的には%>%を使ってスピードに顕著な影響はないと考えました。しかし、この場合は4倍遅くなります。パイプなしdplyrパイプ(%>%)を同等の非パイプ式より遅く使用するのはなぜですか?

library(dplyr) 
library(microbenchmark) 

set.seed(0) 
dummy_data <- dplyr::data_frame(
    id=floor(runif(100000, 1, 100000)) 
    , label=floor(runif(100000, 1, 4)) 
) 

microbenchmark(dummy_data %>% group_by(id) %>% summarise(list(unique(label)))) 
microbenchmark(dummy_data %>% group_by(id) %>% summarise(label %>% unique %>% list)) 

:パイプ付き

min  lq  mean median  uq  max neval 
1.691441 1.739436 1.841157 1.812778 1.880713 2.495853 100 

min  lq  mean median  uq  max neval 
6.753999 6.969573 7.167802 7.052744 7.195204 8.833322 100 

なぜ%>%は、このような状況でそんなに遅いですか?これを書くには良い方法がありますか?以前は「無視できる」の時間に依存しているワンライナーを書くときに、実世界の完全なアプリケーションではほとんど影響かもしれない何

+7

ユニットを離れるべきではありません。この場合、おそらくミリ秒またはマイクロ秒ということになります。 –

+4

2つのスニペットを比較しようとしている場合は、同じ 'microbenchmark'呼び出しで両方のスニペットを実行してください:' microbenchmark(code1 = {... first snippet ...}、code2 = {... second snippet ...} ) '(または名前なしで)、時間を直接比較することができます。 – alistaire

答えて

26

は無視できなくなります。

> set.seed(99);z=sample(10000,4,TRUE) 
> microbenchmark(z %>% unique %>% list, list(unique(z))) 
Unit: microseconds 
        expr  min  lq  mean median  uq  max neval 
z %>% unique %>% list 142.617 144.433 148.06515 145.0265 145.969 297.735 100 
     list(unique(z)) 9.289 9.988 10.85705 10.5820 11.804 12.642 100 

これはあなたのコードに少し違う何かをすることが、ポイントを示している:私はそうと同様のマイクロベンチマークの何かができ、あなたのテストをプロファイルならば、ほとんどの時間はsummarize句になります疑い。パイプはゆっくりです。

パイプはRの呼び出しを、評価関数が使用しているのと同じものに再構成する必要があるため、それらを評価する必要があるためです。したがって、になります。どのくらいの速さに依存する関数は、どのように高速です。 uniquelistへの通話はRで非常に速いので、ここでの違いはパイプオーバーヘッドです。

このようなプロファイリング表現はほとんどの時間は、パイプ機能に費やされている私を示した。その後、

      total.time total.pct self.time self.pct 
"microbenchmark"    16.84  98.71  1.22  7.15 
"%>%"       15.50  90.86  1.22  7.15 
"eval"       5.72  33.53  1.18  6.92 
"split_chain"     5.60  32.83  1.92 11.25 
"lapply"      5.00  29.31  0.62  3.63 
"FUN"       4.30  25.21  0.24  1.41 
..... stuff ..... 

どこかダウンについての15位で、実際の作業が行われます:

"as.list"      1.40  8.13  0.66  3.83 
"unique"      1.38  8.01  0.88  5.11 
"rev"       1.26  7.32  0.90  5.23 

かの一方チャンバーが意図したとおりの機能を呼び出すだけで、Rはそれにまっすぐに下がります:

      total.time total.pct self.time self.pct 
"microbenchmark"    2.30  96.64  1.04 43.70 
"unique"      1.12  47.06  0.38 15.97 
"unique.default"    0.74  31.09  0.64 26.89 
"is.factor"     0.10  4.20  0.10  4.20 

それで、あなたの脳が鎖で考えているコマンドラインではパイプは大丈夫だとは言われていますが、タイムクリティカルな機能ではないとは言えません。実際には、このオーバーヘッドはおそらくglmへの呼び出しで数百データポイントで一掃されますが、それは別の物語です...

+5

FWIW、 'library(pipeR); z%>>%unique%>>%list'は同じことを行い、 'magrittr'バージョンよりも約4倍高速ですが、まだ純粋なベースよりも遅いです。 – BrodieG

+3

機能パッケージから、 'Compose'はより速い' library(functional); (ベンチマークでは6倍も遅いですが)、microbenchmark(mag = z%>%unique%>%list、base = list(ユニーク(z))、fun = Compose(ユニーク、リスト) – Frank

関連する問題