8

重複の可能性:
Rのアプライファミリーはシンタックスシュガー以上のものですか

タイトルが言う通り。ばかげた質問かもしれませんが、私の理解では、「適用」関数を使用する場合、反復はRパーサーではなくコンパイル済みコードで実行されます。これは、たとえば、反復が非常に多く、各操作が比較的単純な場合にのみ、lapplyが「for」ループよりも高速であることを意味しているように思われます。たとえば、lapplyでラップされた関数の1回の呼び出しに10秒かかり、たとえば12回の反復しかない場合、「for」と「lapply」の使用にほとんど違いはないと思います。

考えてみると、とにかく「lapply」内の関数を解析する必要がある場合、コンパイルされた関数がある場合を除いて、「for」ではなく「lapply」を使用することでパフォーマンスが向上するのはなぜですか。 (合計や乗算など)?

前もって感謝します!

ジョシュ

4

2 に答える 2

14

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()

于 2011-06-23T22:35:50.927 に答える
3

Burns'R Inferno(pdf)、p25から:

for各反復が重要なタスクである場合は、明示的なループを使用します。ただし、関数を使用すると、単純なループをより明確かつコンパクトに表現できapply ます。このルールには少なくとも1つの例外があります...結果がリストになり、一部のコンポーネントがリストになる可能性があるNULL場合、forループは問題(大きな問題)でありlapply、期待される答えを提供します。

于 2011-06-24T10:16:58.223 に答える