apply
ループよりもファミリ関数を好む理由for
、またはその逆の理由はいくつかあります。
まず、for()
とapply()
は、sapply()
正しく実行された場合、一般的に互いに同じくらい速くなります。lapply()
R内部のコンパイル済みコードで他の関数よりも多くの動作を行うため、これらの関数よりも高速になります。データを「ループ」する動作が計算時間の重要な部分である場合、速度の利点が最大になるようです。多くの一般的な日常の使用では、本質的に速いことから多くを得る可能性は低いlapply()
です。最終的に、これらはすべてR関数を呼び出すため、解釈して実行する必要があります。
for()
ループが普及しているプログラミングのバックグラウンドを持っている場合は特に、ループの実装が簡単になることがよくあります。apply
ループでの作業は、反復計算をファミリー関数の1つに強制するよりも自然な場合があります。ただし、for()
ループを適切に使用するには、ストレージをセットアップし、ループの出力を再度接続するための管理を行うために、追加の作業を行う必要があります。関数はapply
自動的にこれを行います。例えば:
IN <- runif(10)
OUT <- logical(length = length(IN))
for(i in IN) {
OUT[i] <- IN > 0.5
}
これはベクトル化された演算子と同じようにばかげた例>
ですが、私は何かを指摘したかったのです。つまり、出力を管理する必要があるということです。主なことは、for()
ループでは、ループを開始する前に、出力を保持するのに十分なストレージを常に割り当てることです。必要なストレージの量がわからない場合は、適切なストレージチャンクを割り当て、ループでそのストレージを使い果たしたかどうかを確認し、別の大きなストレージチャンクを追加します。
私の考えでは、関数ファミリーの1つを使用する主な理由apply
は、よりエレガントで読みやすいコードのためです。(上記のように)出力ストレージを管理してループを設定するのではなく、Rにそれを処理させ、データのサブセットに対して関数を実行するように簡潔にRに要求することができます。少なくとも私にとっては、スピードは通常決定には入りません。コードが何であるかを思い出せない場合は、常に最速の関数を選択することで節約するよりもはるかに多くの時間を浪費する可能性が高いため、状況に最も適した関数を使用し、シンプルで理解しやすいコードになります。 1日または1週間以上後にやってください!
このapply
ファミリは、スカラー演算またはベクトル演算に適しています。ループは、同じインデックスを使用して複数の反復操作を実行するのfor()
に役立つことがよくありますi
。たとえば、for()
ループを使用してオブジェクトのk分割またはブートストラップ相互検証を行うコードを作成しました。apply
各CV反復には複数の操作が必要であり、現在のフレーム内の多くのオブジェクトにアクセスし、反復の出力を保持するいくつかの出力オブジェクトを入力するため、ファミリの1つでこれを行うことはおそらく決して楽しいことではありません。
最後のポイントに関しては、なぜlapply()
それよりも速くなる可能性があるのfor()
かについてapply()
、「ループ」はインタプリタされたRコードまたはコンパイルされたコードで実行できることを理解する必要があります。はい、どちらも解釈が必要なR関数を呼び出しますが、ループを実行し、コンパイルされたCコード(たとえば)から直接呼び出す場合はlapply()
、ここでパフォーマンスが向上apply()
します。for()
実際のRコードで。のソースを参照して、それがループapply()
のラッパーであることを確認してから、のコードを確認してください。これは次のとおりです。for()
lapply()
> lapply
function (X, FUN, ...)
{
FUN <- match.fun(FUN)
if (!is.vector(X) || is.object(X))
X <- as.list(X)
.Internal(lapply(X, FUN))
}
<environment: namespace:base>
lapply()
そして、とfor()
と他のapply
ファミリ関数の間で速度に違いがある理由を理解する必要があります。これ.Internal()
は、R自体が使用するコンパイル済みCコードを呼び出すRの方法の1つです。操作との健全性チェックを除いて、FUN
計算全体はCで行われ、R関数を呼び出しますFUN
。それをのソースと比較してくださいapply()
。