6

時間の経過とともにデータ構造を成長させるのではなく、パフォーマンスのために書き込みたい事前に割り当てられたデータ構造があるとします。最初に、sapplyを使用してこれを試しました:

set.seed(1)
count <- 5
pre <- numeric(count)

sapply(1:count, function(i) {
  pre[i] <- rnorm(1)
})
pre
# [1] 0 0 0 0 0


for(i in 1:count) {
  pre[i] <- rnorm(1)
}
pre
# [1] -0.8204684  0.4874291  0.7383247  0.5757814 -0.3053884

これは、の無名関数がsapply別のスコープ (または R の環境ですか?) にあり、結果としてpreオブジェクトが同じではないためだと思います。for ループは同じスコープ/環境に存在するため、期待どおりに機能します。

私は通常、適用関数と for を使用した反復に R メカニズムを採用しようとしましたが、ここではそれを回避する方法がわかりません。私がやるべきこと、またはこのタイプの操作のためのより良いイディオムはありますか?

前述のように、私の例は非常に不自然であり、通常の逸脱を生成することに興味はありません。代わりに、実際のコードは 4 列 150 万行のデータフレームを処理しています。以前は、最終的なデータフレームを取得するために成長とマージに頼っていましたが、マージを避け、ベンチマークに基づいて事前に割り当てることにしました。

4

4 に答える 4

7

sapplyそのように使用することを意図したものではありません。結果はすでに事前に割り当てられています。

とにかく、for ループがパフォーマンス低下の原因である可能性は低いです。おそらく、data.frame を繰り返しサブセット化しているためです。例えば:

set.seed(21)
N <- 1e4
d <- data.frame(n=1:N, s=sample(letters, N, TRUE))
l <- as.list(d)
set.seed(21)
system.time(for(i in 1:N) { d$n[i] <- rnorm(1); d$s <- sample(letters,1) })
#   user  system elapsed 
#   6.12    0.00    6.17 
set.seed(21)
system.time(for(i in 1:N) { l$n[i] <- rnorm(1); l$s <- sample(letters,1) })
#   user  system elapsed 
#   0.14    0.00    0.14 
D <- as.data.frame(l, stringsAsFactors=FALSE)
identical(d,D)
# [1] TRUE

したがって、個々のベクトルをループして、ループの後にそれらを data.frame に結合する必要があります。

于 2012-10-19T13:57:46.827 に答える
3

ファミリは、変数のapply状態を変更するなど、副作用を生成するタスクを意図していません。これらの関数は単に値を返すことを意図しており、それを変数に代入します。これは、R が部分的に同意している機能パラダイムと一致しています。これらの機能を意図したとおりに使用している場合、事前割り当てはあまり発生しません。これは、それらの魅力の一部です。事前に割り当てずにこれを簡単に行うことができます: p <- sapply(1:count, function(i) rnorm(1)). しかし、この例は少し人工的です---p <- rnorm(5)あなたが使用するものです。

実際の問題がこれとは異なり、効率に問題がある場合は、 を調べてvapplyください。に似sapplyていますが、結果のデータ型を指定できるため、速度が向上します。それでも問題が解決しない場合は、パッケージdata.tableまたはff.

于 2012-10-19T12:21:54.133 に答える
2

はい、基本的preに、最後の評価の結果 (長さ 1 のベクトル) を返す無名関数にローカルな a を変更しているためsapply()、正しい解をベクトルとして返します (個々の長さ 1 のベクトルを累積するため)。ただしpre、グローバル ワークスペースでは変更されません。

<<-演算子を使用してこれを回避できます。

set.seed(1)
count <- 5
pre <- numeric(count)

sapply(1:count, function(i) {
  pre[i] <<- rnorm(1)
})
> pre
[1] -0.6264538  0.1836433 -0.8356286  1.5952808  0.3295078

これは変更されましpreたが、さまざまな理由でこれを行うことは避けます。

preこの場合、ケースの事前割り当てから得られることはあまりないと思いますsapply()


また、この例では、どちらも非常に非効率的です。乱数rnorm()を生成するだけです。countしかし、この例は要点を説明するためのものだったと思いますか?

于 2012-10-19T12:21:41.123 に答える
1

何を尋ねているのかわかりません。この場合の sapply の伝統的なイディオムは次のようになります。

pre <- sapply( 1:count, function(x) rnorm(1) )

事前に割り当てる必要はまったくありませんが、事前に割り当てられた変数の使用に制限はありません。

変更したい実際のループを設定すると、物事がより明確になると思います。あなたはパフォーマンスの問題を抱えていると言いますが、ここで多くのことを本当に最適化できる答えが得られるかもしれません. そのような挑戦が大好きな回答者が数人います。

また、長い関数またはループがあるように聞こえます。apply ファミリ関数は、主に表現力を高めるためのものであり、ベクトル化された関数と表現できないものを混在させる場合に、表現力をより明確にすることができます。ベクトル化された関数と混合された複数の小さなsapply呼び出しは、R の 1 つの大きなループよりもはるかに高速です。

于 2012-10-19T12:39:00.857 に答える