6

このRコードをより効率的にするのを手伝ってくれる人はいますか?

文字列のリストを文字列のベクトルに変更する関数、または数値のリストを数値のベクトルに変更する関数、型指定された要素のリストを特定の型のベクトルに変更する関数を作成しようとしています。

リストに次のプロパティがある場合、リストを特定のタイプのベクトルに変更できるようにしたい:

  1. それらは均一に型付けされます。リストのすべての要素は、「文字」または「複合」などのタイプです。

  2. リストの各要素の長さは 1 です。

    as_atomic <- local({
    
        assert_is_valid_elem <- function (elem, mode) {
    
            if (length(elem) != 1 || !is(elem, mode)) {
                stop("")
            }
            TRUE
        }
    
        function (coll, mode) {
    
            if (length(coll) == 0) {
                vector(mode)
            } else {
                # check that the generic vector is composed only
                # of length-one values, and each value has the correct type.
    
                # uses more memory that 'for', but is presumably faster.
                vapply(coll, assert_is_valid_elem, logical(1), mode = mode)
    
                as.vector(coll, mode = mode)
            }
        }
    })
    

例えば、

as_atomic(list(1, 2, 3), 'numeric')
as.numeric(c(1,2,3))

# this fails (mixed types)
as_atomic( list(1, 'a', 2), 'character' )
# ERROR.

# this fails (non-length one element)
as_atomic( list(1, c(2,3,4), 5), 'numeric' )
# ERROR.

# this fails (cannot convert numbers to strings)
as_atomic( list(1, 2, 3), 'character' )
# ERROR.

上記のコードは正常に動作しますが、非常に遅く、関数の動作を変更せずに最適化する方法がわかりません。関数「as_atomic」がそのように動作することが重要です。よく知っている基本機能 (たとえば、リスト解除) に切り替えることができません。これは、不適切なリストに対してエラーをスローする必要があるためです。

require(microbenchmark)

microbenchmark(
    as_atomic( as.list(1:1000), 'numeric'),
    vapply(1:1000, identity, integer(1)),
    unit = 'ns'
)

私の (かなり高速な) マシンでは、ベンチマークの周波数は約 40 Hz であるため、この関数はほとんどの場合、私のコードではレート制限になっています。vapply コントロール ベンチマークの周波数は約 1650Hz ですが、それでもかなり遅いです。

この操作の効率を劇的に改善する方法はありますか? アドバイスをいただければ幸いです。

説明や編集が必要な場合は、下にコメントを残してください。

編集:

皆さんこんにちは、

お返事が大変遅くなり申し訳ありません;;これを再実装する前に、必要な試験がありました。

パフォーマンスのヒントをありがとうございました。単純な R コードを使用して、ひどい 40hz から許容範囲内の 600hz にパフォーマンスを上げました。

is; の代わりに typeof または mode を使用することで、最大のスピードアップが実現しました。これにより、タイトな内部チェック ループが実際に高速化されました。

ただし、実際にパフォーマンスを向上させるには、おそらく弾丸をかじってrcppでこれを書き直す必要があります。

4

3 に答える 3

4

試す:

as_atomic_2 <- function(x, mode) {
  if(!length(unique(vapply(x, typeof, ""))) == 1L) stop("mixed types")
  as.vector(x, mode)
}
as_atomic_2(list(1, 2, 3), 'numeric')
# [1] 1 2 3
as_atomic_2(list(1, 'a', 2), 'character')
# Error in as_atomic_2(list(1, "a", 2), "character") : mixed types
as_atomic_2(list(1, c(2,3,4), 5), 'numeric' )
# Error in as.vector(x, mode) : 
#   (list) object cannot be coerced to type 'double'

microbenchmark(
  as_atomic( as.list(1:1000), 'numeric'),
  as_atomic_2(as.list(1:1000), 'numeric'),
  vapply(1:1000, identity, integer(1)),
  unit = 'ns'
)    
# Unit: nanoseconds
#                                     expr      min       lq     median 
#    as_atomic(as.list(1:1000), "numeric") 23571781 24059432 24747115.5 
#  as_atomic_2(as.list(1:1000), "numeric")  1008945  1038749  1062153.5 
#     vapply(1:1000, identity, integer(1))   719317   762286   778376.5 
于 2014-03-19T19:02:03.390 に答える
3

型チェックを行う独自の関数を定義することがボトルネックのようです。組み込み関数の 1 つを使用すると、大幅に高速化されます。ただし、呼び出しは多少変更されます (ただし、変更することは可能かもしれません)。以下の例は、私が思いついた最速のバージョンです。

を使用して述べたようis.numericに、is.character最大​​のスピードアップが得られます。

as_atomic2 <- function(l, check_type) {
  if (!all(vapply(l, check_type, logical(1)))) stop("")
  r <- unlist(l)
  if (length(r) != length(l)) stop("")
  r
} 

以下は、元のインターフェイスを使用して思いついた最速のものです。

as_atomic3 <- function(l, type) {
  if (!all(vapply(l, mode, character(length(type))) == type)) stop("")
  r <- unlist(l)
  if (length(r) != length(l)) stop("")
  r
}

オリジナルに対するベンチマーク:

res <- microbenchmark(
    as_atomic( as.list(1:1000), 'numeric'),
    as_atomic2( as.list(1:1000), is.numeric),
    as_atomic3( as.list(1:1000), 'numeric'),
    unit = 'ns'
)
#                                    expr      min         lq     median         uq      max neval
#   as_atomic(as.list(1:1000), "numeric") 13566275 14399729.0 14793812.0 15093380.5 34037349   100
# as_atomic2(as.list(1:1000), is.numeric)   314328   325977.0   346353.5   369852.5   896991   100
#  as_atomic3(as.list(1:1000), "numeric")   856423   899942.5   967705.5  1023238.0  1598593   100
于 2014-03-19T19:29:30.883 に答える