web-dev-qa-db-ja.com

Rでは、データフレームの行を本当に高速にループするにはどうすればよいですか?

多数の行と多数の列を持つデータフレームがあるとします。

列には名前があります。行には番号で、列には名前でアクセスします。

たとえば、行をループする1つの(おそらく遅い)方法は次のとおりです。

for (i in 1:nrow(df)) {
  print(df[i, "column1"])
  # do more things with the data frame...
}

別の方法は、個別の列に「リスト」を作成することです(column1_list = df[["column1"])、および1つのループでリストにアクセスします。このアプローチは高速かもしれませんが、多くの列にアクセスしたい場合にも不便です。

データフレームの行をループする高速な方法はありますか?高速ループには他のデータ構造が適していますか?

29
Winston C. Yang

コメントを追跡するのが難しく、これに関するコメントをすでに失ったため、これを完全な答えにする必要があると思います...違いを示す nullglob の例があります中でも、他の例よりもはるかに優れたファミリー関数を適用します。非常に遅いように関数を作成すると、すべての速度が消費され、ループのバリエーションの違いを見つけることができません。しかし、関数を些細なものにすると、ループが物事にどの程度影響するかを確認できます。

また、他の例で未調査のapplyファミリーの一部のメンバーに興味深いパフォーマンス特性があることを付け加えます。最初に、nullglobの相対結果の複製を自分のマシンで表示します。

n <- 1e6
system.time(for(i in 1:n) sinI[i] <- sin(i))
  user  system elapsed 
 5.721   0.028   5.712 

lapply runs much faster for the same result
system.time(sinI <- lapply(1:n,sin))
   user  system elapsed 
  1.353   0.012   1.361 

彼はまた、sapplyがはるかに遅いことを発見しました。以下に、テストされていない他のいくつかを示します。

単純に古いデータのマトリックスバージョンに適用...

mat <- matrix(1:n,ncol =1),1,sin)
system.time(sinI <- apply(mat,1,sin))
   user  system elapsed 
  8.478   0.116   8.531 

したがって、apply()コマンド自体はforループよりも大幅に遅くなります。 (forループは、sin(mat [i、1])を使用しても、それほど遅くなりません。

他の投稿でテストされていないように見えるもう1つのものはtapplyです。

system.time(sinI <- tapply(1:n, 1:n, sin))
   user  system elapsed 
 12.908   0.266  13.589 

もちろん、この方法でタップリを使用することは決してありません。ほとんどの場合、ユーティリティはそのような速度の問題をはるかに超えています。

14
John

最速の方法は、not loop(つまり、ベクトル化された操作)です。ループする必要がある唯一のインスタンスの1つは、依存関係がある場合です(つまり、1つの反復が別の反復に依存します)。それ以外の場合は、ループ外で可能な限りベクトル化された計算を実行してください。

ループする必要があるdoの場合、forループを使用することは、本質的に他のもの(lapply少し速くなりますが、 他のapply関数はforとほぼ同じ速度になる傾向があります )。

12
Shane

Data.framesは本質的に列ベクトルのリストであるという事実を利用して、do.callを使用して、data.frameの各列に列数のアリティを持つ関数を適用できます(リストの「圧縮」に似ています)他の言語で)。

do.call(paste, data.frame(x=c(1,2), z=c("a","b"), z=c(5,6)))
0
Mark B.