7

最終結果の数が事前にわからない場合、R のループで結果を収集する慣用的な方法は何ですか? おもちゃの例を次に示します。

results = vector('integer')
i=1L
while (i < bigBigBIGNumber)  {
    if (someCondition(i)) results = c(results, i)
    i = i+1
}
results

この例の問題点は、追加のたびにベクトルを再割り当てする必要があるため、二次的な複雑さがあることです (私は推測します)。(これは正しいですか?) これを回避する解決策を探しています。

が見つかりましたが、メモリを節約するために避けたいFilter事前生成が必要です。1:bigBigBIGNumber(質問:for (i in 1:N)事前に生成1:Nしてメモリに保持しますか?)

次のようなリンクされたリストのようなものを作成できます。

results = list()
i=1L
while (i < bigBigBIGNumber)  {
    if (someCondition(i)) results = list(results, i)
    i = i+1
}
unlist(results)

(これは連結ではないことに注意してください。 のような構造を構築し、list(list(list(1),2),3)で平坦化していunlistます。)

これよりも良い方法はありますか?通常使用される慣用的な方法は何ですか? (私はRに非常に慣れていません。)この種の問題に取り組む方法についての提案を探しています。コンパクト (書きやすい) と高速なコードの両方に関する提案は大歓迎です! (しかし、私は高速でメモリ効率の良いことに焦点を当てたいと思います。)

4

4 に答える 4

4

おそらく、許容できる最大サイズがあります。事前に割り当ててそのレベルまで満たしてから、必要に応じてトリミングします。これにより、少量の追加メモリが必要な場合でも、サイズを 2 倍にする要求を満たすことができないというリスクが回避されます。早期に失敗し、log(n) の再割り当てではなく 1 つだけを伴います。以下は、最大サイズを取る関数、生成関数、および生成するものが残っていない場合に生成関数が返すトークンです。返す前に最大 n 個の結果を取得します

filln <-
    function(n, FUN, ..., RESULT_TYPE="numeric", DONE_TOKEN=NA_real_)
{
    results <- vector(RESULT_TYPE, n)
    i <- 0L
    while (i < n) {
        ans <- FUN(..., DONE_TOKEN=DONE_TOKEN)
        if (identical(ans, DONE_TOKEN))
            break
        i <- i + 1L
        results[[i]] <- ans
    }

    if (i == n)
        warning("intolerably large result")
   else length(results) <- i
   results
}

ジェネレーターはこちら

fun <- function(thresh, DONE_TOKEN) {
    x <- rnorm(1)
    if (x > thresh) DONE_TOKEN else x
}

そして活動中

> set.seed(123L); length(filln(10000, fun, 3))
[1] 163
> set.seed(123L); length(filln(10000, fun, 4))
[1] 10000
Warning message:
In filln(10000, fun, 4) : intolerably large result
> set.seed(123L); length(filln(100000, fun, 4))
[1] 23101

必要なスペースの量を事前に知っているものと比較することで、おおよそのオーバーヘッドをベンチマークできます。

f1 <- function(n, FUN, ...) {
    i <- 0L
    result <- numeric(n)
    while (i < n) {
        i <- i + 1L
        result[i] <- FUN(...)
    }
    result
}

ここでは、単一の結果のタイミングと値を確認します

>     set.seed(123L); system.time(res0 <- filln(100000, fun, 4))
   user  system elapsed 
  0.944   0.000   0.948 
>     set.seed(123L); system.time(res1 <- f1(23101, fun, 4))
   user  system elapsed 
  0.688   0.000   0.689 
> identical(res0, res1)
[1] TRUE

もちろん、この例では、単純なベクトル解によって影が薄くなります

set.seed(123L); system.time(res2 <- rnorm(23101))
identical(res0, res2)
于 2013-05-13T00:00:11.890 に答える
1

あなたがリストした2番目のものに近い:

  results <- list()
  for (i in ...)  {
      ...
     results[[i]]  <- ...
 }

iである必要はなく、 などintegerでもよいことに注意してください。character

また、 results[[length(results)]] <- ... 必要に応じて使用できますが、イテレータが既にある場合は、おそらくそうではありません。

于 2013-05-12T21:57:19.960 に答える