web-dev-qa-db-ja.com

ここで再び行きます:Rのリストに要素を追加します

償却された一定時間内にRのリストにオブジェクトを追加しますか? に対する受け入れられた答えに満足できません

_> list1 <- list("foo", pi)
> bar <- list("A", "B")
_

新しい要素barを_list1_に追加するにはどうすればよいですか?明らかに、c()は機能せず、barを平坦化します。

_> c(list1, bar)
[[1]]
[1] "foo"

[[2]]
[1] 3.141593

[[3]]
[1] "A"

[[4]]
[1] "B"
_

インデックスへの割り当てが機能する:

_> list1[[length(list1)+1]] <- bar
> list1
[[1]]
[1] "foo"

[[2]]
[1] 3.141593

[[3]]
[[3]][[1]]
[1] "A"

[[3]][[2]]
[1] "B"
_

この方法の効率はどのくらいですか?もっとエレガントな方法はありますか?

47
user443854

リストに要素を追加すると、一度に1つの要素を実行すると非常に遅くなります。次の2つの例を参照してください。

グローバル環境にResult変数を保持して評価フレームへのコピーを回避し、.GlobalEnv$でブラインド検索を回避するために<<-でそれを探す場所をRに指示しています。

Result <- list()

AddItemNaive <- function(item)
{
    .GlobalEnv$Result[[length(.GlobalEnv$Result)+1]] <- item
}

system.time(for(i in seq_len(2e4)) AddItemNaive(i))
#   user  system elapsed 
#  15.60    0.00   15.61 

スロー。次に、2番目のアプローチを試してみましょう。

Result <- list()

AddItemNaive2 <- function(item)
{
    .GlobalEnv$Result <- c(.GlobalEnv$Result, item)
}

system.time(for(i in seq_len(2e4)) AddItemNaive2(i))
#   user  system elapsed 
#  13.85    0.00   13.89

まだ遅い。

ここで、environmentを使用して、リストに要素を追加する代わりに、この環境内で新しい変数を作成してみましょう。ここでの問題は、変数に名前を付ける必要があることです。そのため、各アイテムに「スロット」という名前を付ける文字列としてカウンターを使用します。

Counter <- 0
Result <- new.env()

AddItemEnvir <- function(item)
{
    .GlobalEnv$Counter <- .GlobalEnv$Counter + 1

    .GlobalEnv$Result[[as.character(.GlobalEnv$Counter)]] <- item
}

system.time(for(i in seq_len(2e4)) AddItemEnvir(i))
#   user  system elapsed 
#   0.36    0.00    0.38 

はるかに高速です。 :-)動作するのは少し厄介かもしれませんが、動作します。

最後のアプローチではリストを使用しますが、サイズを一度に1要素ずつ増やす代わりに、リストがいっぱいになるたびにサイズをdoublesにします。リストサイズは、lengthを使用した速度低下を避けるために、専用の変数にも保持されます。

Counter <- 0
Result <- list(NULL)
Size <- 1

AddItemDoubling <- function(item)
{
    if( .GlobalEnv$Counter == .GlobalEnv$Size )
    {
        length(.GlobalEnv$Result) <- .GlobalEnv$Size <- .GlobalEnv$Size * 2
    }

    .GlobalEnv$Counter <- .GlobalEnv$Counter + 1

    .GlobalEnv$Result[[.GlobalEnv$Counter]] <- item
}

system.time(for(i in seq_len(2e4)) AddItemDoubling(i))
#   user  system elapsed 
#   0.22    0.00    0.22

さらに高速です。そして、リストと同じように簡単に作業できます。

繰り返しを増やしながら、最後の2つのソリューションを試してみましょう。

Counter <- 0
Result <- new.env()

system.time(for(i in seq_len(1e5)) AddItemEnvir(i))
#   user  system elapsed 
#  27.72    0.06   27.83 


Counter <- 0
Result <- list(NULL)
Size <- 1

system.time(for(i in seq_len(1e5)) AddItemDoubling(i))
#   user  system elapsed 
#   9.26    0.00    9.32

さて、最後の1つは明確に進むべき道です。

50
Ferdinand.kraft

それは非常に簡単です。次の方法で追加するだけです。

list1$bar <- bar
19
PAC

Rのリスト/ベクトルの長さを変更する操作は、常にすべての要素を新しいリストにコピーするため、O(n)のように遅くなります。環境への保存はO(1)ですが、一定のオーバーヘッドがより高くなります。実際のO(1) https://stackoverflow.com/a/32870310/264177 にある他の質問に対する私の答えをご覧ください。

6
JanKanis